- Published on
Use SQLite in a Vercel Serverless Function (Yes, It Works)
- Authors
- Name
- Ahmed Farid
- @
TIP
Vercel Functions get 50 MB read-only filesystem at runtime—plenty of room for a SQLite database.
In this guide you will:
- Create a SQLite schema and seed data locally.
- Bundle
data.db
into the Vercel deployment layer. - Query it from an Edge Function using
@vercel/sqlite
(better-sqlite3 WebAssembly build). - Optionally mutate data with D1-like durability using Blob Storage snapshot.
Table of Contents
- Table of Contents
- 1. Why SQLite on Vercel?
- 2. Project Setup
- 3. Create Database Locally
- 4. Add to .vercelignore
- 5. API Route: /api/posts
- 6. Mutations (Optional)
- 7. Deploy
- 8. Benchmark vs PlanetScale
- 9. Caveats
- 10. Further Reading
- 11. Conclusion
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/
).
.vercelignore
4. Add to We include the db by default—the build output keeps it read-only. If you have giant assets, add exclusions.
/api/posts
5. API Route: 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:
- Perform writes.
- After commit, upload the updated db to Vercel Blob Storage:
await put('data.db', Bun.file('./data.db'));
- 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
Metric | SQLite (Edge) | PlanetScale (MySQL, US-EAST) |
---|---|---|
p95 latency | 45 ms | 220 ms |
QPS limit | 1 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. 🚀