Skip to content

astrojarred/sveltekit-python-vercel

 
 

Repository files navigation

image image image

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
  • Write server load functions in +page.server.py and +layout.server.py
  • Deploy automatically to Vercel Serverless (Python 3.12 runtime)

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({
      plugins: [sveltekit(), ...(await sveltekit_python_vercel())],
    });
  • Update your svelte.config.js:

    import adapter from "@sveltejs/adapter-vercel";
    import { vitePreprocess } from "@sveltejs/kit/vite";
    
    /** @type {import('@sveltejs/kit').Config} */
    const config = {
      preprocess: vitePreprocess(),
      kit: {
        adapter: adapter(),
        moduleExtensions: [".js", ".ts", ".py"], // add ".py" to resolve +server.py endpoints
      },
    };
    
    export default config;
  • Update your vercel.json

    • The build command first runs vite build (which generates .vercel/output/ via the SvelteKit adapter), then runs our script to write the Python function into that same output directory and patch the routing config.
    • No routes or functions keys are needed — routing is handled automatically via the Vercel Build Output API.
    {
      "buildCommand": "vite build; node ./node_modules/sveltekit-python-vercel/esm/src/vite/sveltekit_python_vercel/bin.mjs"
    }
  • Write some +server.py endpoints. See the example section below.

Testing Locally

uv is recommended for managing your Python environment.

  • Run uv init --python 3.12 to create a pyproject.toml pinned to Python 3.12 (the same version Vercel's runtime uses).
  • Add the required packages: uv add fastapi uvicorn
  • Add any other dependencies you need: uv add numpy pandas ...
  • Run your dev server inside uv:
    • uv run pnpm dev
  • You should see both the usual SvelteKit server start and the uvicorn server (by default on http://0.0.0.0:8000) in the console.

Deploying to Vercel

Just push to your repository — no extra steps required.

  • The buildCommand in vercel.json handles everything automatically:
    1. vite build runs the SvelteKit build and writes .vercel/output/ via @sveltejs/adapter-vercel
    2. bin.mjs then writes your Python endpoints into .vercel/output/functions/ using the Build Output API and patches the routing config
  • Your +server.py files and dependency declarations (requirements.txt, pyproject.toml, Pipfile, etc.) are bundled automatically — there is no need to commit an /api folder or manually generate a requirements.txt.
  • Python packages are pre-installed into the function bundle at build time using pip install --target, so they are available in Vercel's raw Python 3.12 Lambda environment without any extra configuration.

Example

  • Frontend: /src/routes/py/+page.svelte

    <script>
      let a = $state(0);
      let b = $state(0);
      let total = $state(0);
    
      async function pyAddPost() {
        const res = await fetch("/py", {
          method: "POST",
          body: JSON.stringify({ a, b }),
          headers: { "content-type": "application/json" },
        });
        total = (await res.json()).sum;
      }
    
      async function pyAddGet() {
        const res = await fetch(`/py?a=${a}&b=${b}`);
        total = (await res.json()).sum;
      }
    </script>
    
    <h1>SvelteKit page with a Python backend</h1>
    
    <label>a: <input type="number" bind:value={a} /></label>
    <label>b: <input type="number" bind:value={b} /></label>
    
    <button type="button" onclick={pyAddPost}>POST</button>
    <button type="button" onclick={pyAddGet}>GET</button>
    
    <p>Total: {total}</p>
  • 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

  • GET endpoints receive query parameters directly as function arguments. Type annotations are used for coercion (e.g. a: float parses ?a=3 as 3.0).
  • All other HTTP methods receive the request body as JSON. The recommended pattern is a Pydantic model as the single argument — FastAPI handles validation and parsing automatically.

Python load functions

Server-side load functions work in +page.server.py and +layout.server.py. No extra Python package install is required — the runtime is bundled automatically like +server.py.

async def load(event):
    if event.params["id"] == "secret":
        return ("redirect", 307, "/demo/public")

    if event.params["id"] not in ("public", "1", "2"):
        return ("error", 404, "Not found")

    return {
        "title": f"Item {event.params['id']}",
        "parent_theme": event.parent.theme if event.parent else None,
    }

Errors and redirects can also use injected helpers (no import needed):

async def load(event):
    if not event.cookies.get("session"):
        redirect(307, "/login")
    return {"ok": True}

Available on event: params, url, route, parent (layout data), data (from a sibling universal load), cookies.

Current limitations: no event.fetch, setHeaders, depends, or page options (prerender, etc.) in .py files yet.

npm channels

Tag When it updates Install
latest GitHub Release created pnpm add -D sveltekit-python-vercel
beta Every push to main pnpm add -D sveltekit-python-vercel@beta
pr-<n> Open/update PR #n (same-repo only) pnpm add -D sveltekit-python-vercel@pr-15

Beta versions look like 1.0.3-beta.abc1234 (main) or 1.0.3-beta.pr15.abc1234 (PRs).

All publishes run through .github/workflows/publish.yml because npm trusted publishing only allows one workflow filename per package.

Developing the package locally

If you work on this repo and a consumer app side by side (e.g. test-skpv-deploy):

  1. Build: deno run -A dnt.ts $(npm view sveltekit-python-vercel version) (or any version string)
  2. In the consumer: pnpm add -D sveltekit-python-vercel@link:../sveltekit-python-vercel/npm
  3. Rebuild and restart the consumer dev server after library changes

Commit ^x.y.z or @beta in the consumer for Vercel — link: only works on your machine.

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

  • Add hot reloading in dev mode
  • Generate endpoints automatically during build (via Vercel Build Output API)
    • Auto-bundle requirements.txt / pyproject.toml / Pipfile at build time
  • Add form actions
  • Add load functions
  • Add helper functions to automatically call API endpoints in project

About

Write Python endpoints in SvelteKit and deploy seamlessly to Vercel

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Python 68.9%
  • TypeScript 29.8%
  • Other 1.3%