-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #121 from singlestore-labs/bharath-templates
Add Create DashApp and create Singlestore Cloud Functions template to spaces
- Loading branch information
Showing
4 changed files
with
642 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[meta] | ||
authors=["singlestore"] | ||
title="Publish your first SingleStore Cloud function" | ||
description="""\ | ||
Learn how to connect to SingleStoreDB and perform basic\ | ||
CRUD operations and finally deploy these functions as callable API endpoints. | ||
""" | ||
icon="browser" | ||
difficulty="beginner" | ||
tags=["starter", "notebooks", "python"] | ||
lesson_areas=[] | ||
destinations=["spaces"] | ||
minimum_tier="free-shared" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"id": "8ba141c2-e0c2-4723-b782-c924bc7b294c", | ||
"metadata": {}, | ||
"source": [ | ||
"<div id=\"singlestore-header\" style=\"display: flex; background-color: rgba(235, 249, 245, 0.25); padding: 5px;\">\n", | ||
" <div id=\"icon-image\" style=\"width: 90px; height: 90px;\">\n", | ||
" <img width=\"100%\" height=\"100%\" src=\"https://raw.githubusercontent.com/singlestore-labs/spaces-notebooks/master/common/images/header-icons/browser.png\" />\n", | ||
" </div>\n", | ||
" <div id=\"text\" style=\"padding: 5px; margin-left: 10px;\">\n", | ||
" <div id=\"badge\" style=\"display: inline-block; background-color: rgba(0, 0, 0, 0.15); border-radius: 4px; padding: 4px 8px; align-items: center; margin-top: 6px; margin-bottom: -2px; font-size: 80%\">SingleStore Notebooks</div>\n", | ||
" <h1 style=\"font-weight: 500; margin: 8px 0 0 4px;\">Publish your first SingleStore Cloud function</h1>\n", | ||
" </div>\n", | ||
"</div>" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "cd7deb95-c7bb-48eb-9cab-ed508b3be5ff", | ||
"metadata": {}, | ||
"source": [ | ||
"<div class=\"alert alert-block alert-warning\">\n", | ||
" <b class=\"fa fa-solid fa-exclamation-circle\"></b>\n", | ||
" <div>\n", | ||
" <p><b>Note</b></p>\n", | ||
" <p>This notebook can be run on a Free Starter Workspace. To create a Free Starter Workspace navigate to <tt>Start</tt> using the left nav. You can also use your existing Standard or Premium workspace with this Notebook.</p>\n", | ||
" </div>\n", | ||
"</div>" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"id": "a5564913-7ff8-41bf-b64b-b67971c63fae", | ||
"source": [ | ||
"This Jupyter notebook will help you build your first Cloud Function, showcasing how to leverage the ultra-fast queries of SingleStore to build a responsive API server using FastAPI" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"id": "1e394195-29b4-403c-9abf-5d7731349eb6", | ||
"source": [ | ||
"## Create some simple tables\n", | ||
"\n", | ||
"This setup establishes a basic relational structure to store some items information." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 1, | ||
"id": "a17bdd3a-16b3-4e19-8a56-6566a169eccb", | ||
"outputs": [], | ||
"source": [ | ||
"%%sql\n", | ||
"DROP TABLE IF EXISTS items;\n", | ||
"\n", | ||
"CREATE TABLE IF NOT EXISTS\n", | ||
"items (\n", | ||
" id INT PRIMARY KEY,\n", | ||
" name VARCHAR(255),\n", | ||
" price FLOAT\n", | ||
");" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"id": "af6e2618-de97-4397-b0d2-23e4a4df1d83", | ||
"source": [ | ||
"## Create a Connection Pool\n", | ||
"\n", | ||
"To run multiple simultaneous queries, we use sqlalchemy to create a pool of sql connections to the workspace you have selected. We also define a method to execute queries and transactions using a connection from this pool." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 2, | ||
"id": "f485e71b-2b05-4696-b22a-cf046fd83090", | ||
"outputs": [], | ||
"source": [ | ||
"from sqlalchemy import create_engine, text\n", | ||
"import requests\n", | ||
"\n", | ||
"ca_cert_url = \"https://portal.singlestore.com/static/ca/singlestore_bundle.pem\"\n", | ||
"ca_cert_path = \"/tmp/singlestore_bundle.pem\"\n", | ||
"\n", | ||
"response = requests.get(ca_cert_url)\n", | ||
"with open(ca_cert_path, \"wb\") as f:\n", | ||
" f.write(response.content)\n", | ||
"\n", | ||
"engine = create_engine(\n", | ||
" f\"{connection_url}?ssl_ca={ca_cert_path}\",\n", | ||
" pool_size=10, # Maximum number of connections in the pool is 10\n", | ||
" max_overflow=5, # Allow up to 5 additional connections (temporary overflow)\n", | ||
" pool_timeout=30 # Wait up to 30 seconds for a connection from the pool\n", | ||
")\n", | ||
"\n", | ||
"def execute_query(query: str):\n", | ||
" with engine.connect() as connection:\n", | ||
" return connection.execute(text(query))\n", | ||
"\n", | ||
"def execute_transaction(transactional_query: str):\n", | ||
" with engine.connect() as connection:\n", | ||
" transaction = connection.begin()\n", | ||
" try:\n", | ||
" result = connection.execute(text(transactional_query))\n", | ||
" transaction.commit()\n", | ||
" return result\n", | ||
" except Exception as e:\n", | ||
" transaction.rollback()\n", | ||
" raise e" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"id": "ee9058a9-34a5-46fc-8b12-d30cbb8c3340", | ||
"source": [ | ||
"## Setup Environment\n", | ||
"\n", | ||
"Lets setup the environment ro run a FastAPI app defining the Data Model and an executor to run the different requests in different threads simultaneously" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 3, | ||
"id": "66df8f0c-70c6-4f06-9e64-ef06961cca3a", | ||
"outputs": [], | ||
"source": [ | ||
"from fastapi import FastAPI, HTTPException\n", | ||
"from pydantic import BaseModel\n", | ||
"from singlestoredb import connect\n", | ||
"from concurrent.futures import ThreadPoolExecutor\n", | ||
"import asyncio\n", | ||
"\n", | ||
"# Define the Type of the Data\n", | ||
"class Item(BaseModel):\n", | ||
" id: int\n", | ||
" name: str\n", | ||
" price: float\n", | ||
"\n", | ||
"# Create an executor that can execute queries on multiple threads simultaneously\n", | ||
"executor = ThreadPoolExecutor()\n", | ||
"def run_in_thread(fn, *args):\n", | ||
" loop = asyncio.get_event_loop()\n", | ||
" return loop.run_in_executor(executor, fn, *args)" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"id": "96760949-5ab2-474d-80ca-d23b5dcc52f7", | ||
"source": [ | ||
"## Define FastAPI App\n", | ||
"\n", | ||
"Next, we will be defining a FastAPI app that can insert, query and delete data from your table" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 4, | ||
"id": "3087dbe6-57ce-4410-a42f-5b0fe90add90", | ||
"outputs": [], | ||
"source": [ | ||
"app = FastAPI()\n", | ||
"\n", | ||
"# Get all items\n", | ||
"@app.get(\"/items\", response_model=list[Item])\n", | ||
"async def get_items():\n", | ||
" def get_items_query():\n", | ||
" result = execute_query(\"SELECT * FROM items;\")\n", | ||
" rows = result.fetchall()\n", | ||
" return [{\"id\": row[0], \"name\": row[1], \"price\": row[2]} for row in rows]\n", | ||
"\n", | ||
" try:\n", | ||
" return await run_in_thread(get_items_query)\n", | ||
" except Exception as e:\n", | ||
"\n", | ||
" raise HTTPException(status_code=500, detail=f\"Error fetching all items: {str(e)}\")\n", | ||
"\n", | ||
"# Insert an item\n", | ||
"@app.post(\"/items\", response_model=dict)\n", | ||
"async def create_item(item: Item):\n", | ||
" def insert_item_query():\n", | ||
" result = execute_transaction(f\"INSERT INTO items (id, name, price) VALUES ({item.id}, '{item.name}', {item.price})\")\n", | ||
" return {\"message\": f\"Item with id {item.id} inserted successfully\"}\n", | ||
"\n", | ||
" try:\n", | ||
" return await run_in_thread(insert_item_query)\n", | ||
" except Exception as e:\n", | ||
" raise HTTPException(status_code=500, detail=f\"Error while inserting item with id {item.id}: {str(e)}\")\n", | ||
"\n", | ||
"# Get item by id\n", | ||
"@app.get(\"/items/{item_id}\", response_model=Item)\n", | ||
"async def get_item(item_id: int):\n", | ||
" def get_item_query():\n", | ||
" result = execute_query(f\"SELECT * FROM items WHERE id={item_id}\")\n", | ||
" row = result.fetchone()\n", | ||
" if not row:\n", | ||
" raise HTTPException(status_code=404, detail=\"Item not found\")\n", | ||
" return {\"id\": row[0], \"name\": row[1], \"price\": row[2]}\n", | ||
"\n", | ||
" try:\n", | ||
" return await run_in_thread(get_item_query)\n", | ||
" except HTTPException as e:\n", | ||
" raise e\n", | ||
" except Exception as e:\n", | ||
" raise HTTPException(status_code=500, detail=f\"Error fetching item with id {item_id}: {str(e)}\")\n", | ||
"\n", | ||
"# Delete item by id\n", | ||
"@app.delete(\"/items/{item_id}\", response_model=dict)\n", | ||
"async def delete_item(item_id: int):\n", | ||
" def delete_item_query():\n", | ||
" result = execute_transaction(f\"DELETE FROM items WHERE id={item_id}\")\n", | ||
" return {\"message\": f\"number of rows deleted: {result.rowcount}\"}\n", | ||
"\n", | ||
" try:\n", | ||
" return await run_in_thread(delete_item_query)\n", | ||
" except Exception as e:\n", | ||
" raise HTTPException(status_code=500, detail=f\"Error deleting item with id {item_id}: {str(e)}\")" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"id": "c3d9ed07-4b55-4d17-aabb-e11b399109d1", | ||
"source": [ | ||
"## Start the FastAPI server\n", | ||
"\n", | ||
"The link at which the cloud function will be available interactively will be displayed." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 5, | ||
"id": "ff002c7d-9f1c-40e5-b82a-c9176251dc99", | ||
"outputs": [], | ||
"source": [ | ||
"import singlestoredb.apps as apps\n", | ||
"connection_info = await apps.run_function_app(app)" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"id": "fabe76b7-e6a0-43a0-8d9e-aa79bd7d3021", | ||
"source": [ | ||
"## Publish Cloud Function\n", | ||
"\n", | ||
"After validating the Cloud Function interactively, you can publish it and use it as an API server for your data!" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "386f804b-f3a9-4452-9575-b87c917bbbf8", | ||
"metadata": {}, | ||
"source": [ | ||
"<div id=\"singlestore-footer\" style=\"background-color: rgba(194, 193, 199, 0.25); height:2px; margin-bottom:10px\"></div>\n", | ||
"<div><img src=\"https://raw.githubusercontent.com/singlestore-labs/spaces-notebooks/master/common/images/singlestore-logo-grey.png\" style=\"padding: 0px; margin: 0px; height: 24px\"/></div>" | ||
] | ||
} | ||
], | ||
"nbformat": 4, | ||
"nbformat_minor": 5 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[meta] | ||
authors=["singlestore"] | ||
title="Publish your first SingleStore DashApp" | ||
description="""\ | ||
Learn how to connect to SingleStoreDB\ | ||
and publish an interactive Dashboard. | ||
""" | ||
icon="browser" | ||
difficulty="beginner" | ||
tags=["starter", "notebooks", "python"] | ||
lesson_areas=[] | ||
destinations=["spaces"] | ||
minimum_tier="free-shared" |
Oops, something went wrong.