sveltekit-python-vercel
Write Python endpoints in SvelteKit and seamlessly deploy them to Vercel.
This is very much in beta.
Current Features
- Write
+server.py files nearly the same way you would write +server.js files
- Deploy (quasi) automatically to Vercel Serverless
Installing
-
Open or set up your SvelteKit project
-
Install SvelteKit's Vercel adapter: pnpm i -D @sveltejs/adapter-vercel
-
Install with pnpm i -D sveltekit-python-vercel
-
Update your vite.config.js
import { defineConfig } from "vite";
import { sveltekit } from "@sveltejs/kit/vite";
import { sveltekit_python_vercel } from "sveltekit-python-vercel/vite";
export default defineConfig(({ command, mode }) => {
return {
plugins: [sveltekit_python_vercel(), sveltekit()],
};
});
-
Update your svelte.config.js:
import adapter from "@sveltejs/adapter-vercel";
import { vitePreprocess } from "@sveltejs/kit/vite";
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter(),
moduleExtensions: [".js", ".ts", ".py"],
},
};
export default config;
-
Update your vercel.json
- The build command prepares all your endpoints and copies them to the
/api directory where Vercel looks for functions
- Functions and Routes tell Vercel how to run and redirect function calls
{
"buildCommand": "node ./node_modules/sveltekit-python-vercel/esm/src/vite/sveltekit_python_vercel/bin.mjs; vite build",
"functions": {
"api/**/*.py": {
"runtime": "@vercel/python@3.0.7"
}
},
"routes": [
{
"src": "/api/(.*)",
"dest": "api/index.py"
}
]
}
-
Write some +server.py endpoints. See the example section below.
Testing Locally
Using Poetry to manage your virtual environments with this package is recommended.
-
Run poetry init to create a new virtual environment, and follow the steps. Or simply create a pyproject.toml like the one below.
[tool.poetry]
name = "sveltekit-python-example"
version = "0.1.0"
description = ""
authors = ["Your Name <email@gmail.com>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.9"
fastapi = "^0.95.2"
uvicorn = "^0.22.0"
[tool.poetry.group.dev.dependencies]
watchfiles = "^0.19.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
-
Required packages are python3.9 (that is what Vercel's runtime uses), fastapi, and uvicorn.
-
Install whatever other dependencies you need from pypi using poetry add package-name
-
Enter your virtual env with poetry shell
-
Run pnpm dev or npm dev
- You should see both the usual SvelteKit server start as well as the unvicorn server (by default on
http://0.0.0.0:8000) in the console.
Deploying to Vercel
-
At the moment this requires a tiny bit of extra labor besides just pushing to your repository. I believe this is because of the way Vercel looks for serverless functions, but I hope to make this a bit easier in the future.
-
When you make changes to your python endpoints, you have to manually regenerate the /api folder by running:
poetry export -f requirements.txt --output requirements.txt --without-hashes
node ./node_modules/sveltekit-python-vercel/esm/src/vite/sveltekit_python_vercel/bin.mjs
-
Then commit requirements.txt and the changes in /api and push.
Note:
Example
-
Frontend: /src/routes/py/+page.svelte
<script lang="ts">
let a = 0;
let b = 0;
let total = 0;
async function pyAddPost() {
const response = await fetch("/py", {
method: "POST",
body: JSON.stringify({ a, b }),
headers: {
"content-type": "application/json",
},
});
let res = await response.json();
total = res.sum;
}
async function pyAddGet() {
const response = await fetch(`/py?a=${a}&b=${b}`, {
method: "GET",
headers: {
"content-type": "application/json",
},
});
let res = await response.json();
total = res.sum;
}
</script>
<h1>This is a SvelteKit page with a python backend.</h1>
<h3>POST Example</h3>
<form>
<input type="number" name="a" placeholder="Number 1" bind:value="{a}" />
<input type="number" name="b" placeholder="Number 2" bind:value="{b}" />
<button on:click|preventDefault="{pyAddPost}">Add</button>
</form>
<h4>Total: {total}</h4>
<br />
<h3>GET Example</h3>
<form>
<input type="number" name="a" placeholder="Number 1" bind:value="{a}" />
<input type="number" name="b" placeholder="Number 2" bind:value="{b}" />
<button on:click|preventDefault="{pyAddGet}">Add</button>
</form>
<h4>Total: {total}</h4>
-
Backend: /src/routes/py/+server.py
from pydantic import BaseModel
class NumberSet(BaseModel):
a: float
b: float
async def POST(data: NumberSet):
return {"sum": data.a + data.b}
async def GET(a: float, b: float):
return {"sum": a + b}
Backend Caveats
There are currently a few things that have to be worked around.
GET endpoints are directly fed the parameters from the url, so when you define an endpoint
- All other endpoints are fed the body as a JSON. The recommended way to deal with this is to use a pydantic model and pass it as the singular input to the function.
See the example above.
Fork of sveltekit-modal
Check out the awesome sveltekit-modal package by @semicognitive, the original way to get your python code running in SvelteKit. Modal even has GPU support for running an entire ML stack within SvelteKit.
Possible future plans