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

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.

databasetutorial