{ }
Published on

Use SQLite in a Vercel Serverless Function (Yes, It Works)

Authors
  • avatar
    Name
    Ahmed Farid
    Twitter
    @

TIP

Vercel Functions get 50 MB read-only filesystem at runtime—plenty of room for a SQLite database.

In this guide you will:

  1. Create a SQLite schema and seed data locally.
  2. Bundle data.db into the Vercel deployment layer.
  3. Query it from an Edge Function using @vercel/sqlite (better-sqlite3 WebAssembly build).
  4. Optionally mutate data with D1-like durability using Blob Storage snapshot.

Table of Contents

1. Why SQLite on Vercel?

  • 💸 Free—no external DB instance.
  • Low latency—reads happen inside the function, no TCP hops.
  • 🕶️ Zero DevOps—ship a single file.

Use-cases: read-only content APIs, feature flags, small admin back-office.

2. Project Setup

npx create-vercel-app sqlite-api
cd sqlite-api
npm i @vercel/sqlite@latest

NOTE

@vercel/sqlite bundles better-sqlite3 compiled to WASM, compatible with Edge runtime and Node 20.

3. Create Database Locally

npm i -g sqlite3
sqlite3 data.db <<'SQL'
CREATE TABLE posts(id INTEGER PRIMARY KEY, title TEXT, body TEXT);
INSERT INTO posts(title, body) VALUES ('Hello', 'SQLite on Vercel');
SQL

Place data.db at repo root (outside api/).

4. Add to .vercelignore

We include the db by default—the build output keeps it read-only. If you have giant assets, add exclusions.

5. API Route: /api/posts

api/posts.ts:

import { createConnection } from '@vercel/sqlite'

export const config = { runtime: 'edge' } // also works with 'nodejs'

export default async function handler() {
  const db = await createConnection({ filename: './data.db', readonly: true })
  const rows = db.prepare('SELECT * FROM posts').all()
  return new Response(JSON.stringify(rows), {
    headers: { 'content-type': 'application/json' },
  })
}

Cold start is ~35 ms on Vercel Edge—with WASM init + query.

6. Mutations (Optional)

Writes inside the function modify an ephemeral copy (filesystem is read-write tmpfs but reset every invocation). For durability:

  1. Perform writes.
  2. After commit, upload the updated db to Vercel Blob Storage: await put('data.db', Bun.file('./data.db'));
  3. Fetch latest blob at the beginning of the request and fs.writeFileSync to /tmp/data.db before opening connection.

This pattern keeps persisted snapshot across invocations (eventual consistency).

7. Deploy

vercel --prod

Open https://<project>.vercel.app/api/posts → JSON array.

8. Benchmark vs PlanetScale

MetricSQLite (Edge)PlanetScale (MySQL, US-EAST)
p95 latency45 ms220 ms
QPS limit1 000+plan-dependent

For read-heavy APIs, SQLite wins.

9. Caveats

  • DB file must stay <50 MB for Node, <25 MB for Edge.
  • Concurrency: one write at a time. For heavy writes use external DB.
  • Blob snapshot adds ~30–70 ms latency on write paths.

10. Further Reading

  • Vercel blog: Edge SQLite WASM.
  • LiteFS Cloud (coming soon) for distributed writes.
  • Turso / libSQL remote driver.

11. Conclusion

SQLite isn’t just for mobile—you can ship it with your serverless function and enjoy stellar performance with zero infra. 🚀