database · tutorial
One switch: SQLite on your laptop, MongoDB in prod
Same models, same routes, zero rewrites. How the DB_PROVIDER toggle works.

Boring Team
September 5, 2026 · 1 min read
The most expensive line in most startups is the database migration nobody planned. You start on Postgres, outgrow the free tier, and spend a sprint rewriting every query.
Boring skips that chapter. There is exactly one variable:
DB_PROVIDER=sqlite # laptop: ./db/app.db, zero setup
DB_PROVIDER=mongodb # prod: Atlas via MONGODB_URI
DB_PROVIDER=supabase # prod: Postgres via Supabase
Same function names, everywhere
App code never touches a driver directly. It calls the registry:
import User from "@/models/User";
import Board from "@/models/Board";
const user = await User.findById(id).populate("boards");
await Board.create({ userId: user._id, name: "Roadmap" });
That works on all three backends. Each provider folder — providers/db/sqlite/, providers/db/mongodb/, providers/db/supabase/ — implements the same surface: User, Board, Lead, Post, Vote, Comment, plus connect() and upsertUserByEmail().
Adding a database is one folder + one line
Want PlanetScale next quarter? Create providers/db/planetscale/ with the same exports, then register it:
const REGISTRY = { mongodb, sqlite, supabase, planetscale };
Routes, models, and auth never change. That's the whole trick — and the reason removing a database is just as safe as adding one.
Which should you pick?
- SQLite — solo dev, first 10k users, a $5 VPS. No server, no bill, no pooling bugs.
- MongoDB — you already live in document-land, or need Atlas search and scaling knobs.
- Supabase — you want Postgres plus their dashboard, and don't mind one more account.
Start boring. Switch when the bill — or the traffic — tells you to.