courses··1 min read

Cloudflare D1 in ten minutes

From zero to a working SQLite database on the edge, queried from a Next.js app. Copy-pasteable.

YC

Yunzhui Cai

Published May 10, 2026


D1 is Cloudflare's SQLite-based serverless database. We use it inside Orpheus to store per-tenant transcription metadata, and we use it on Signal (this site) to store newsletter subscribers. This post takes you from wrangler login to running your first query.

Step 1 — create the database

npx wrangler d1 create my-db

Save the database_id it prints. Paste it into wrangler.toml:

[[d1_databases]]
binding = "DB"
database_name = "my-db"
database_id = "00000000-0000-0000-0000-000000000000"

Step 2 — define a schema

CREATE TABLE subscribers (
  email TEXT PRIMARY KEY,
  locale TEXT NOT NULL,
  created_at INTEGER NOT NULL
);

Save as migrations/0000_init.sql, then:

npx wrangler d1 migrations apply my-db --remote

Step 3 — query

const { results } = await env.DB
  .prepare("SELECT * FROM subscribers WHERE locale = ?")
  .bind(locale)
  .all();

That's it. No connection pool, no migrations server, no on-call rotation.

When D1 fits and when it doesn't

Fits: Per-tenant data, user lists, small to medium analytics, anything that benefits from being co-located with your Worker. Up to about a gigabyte per database before you start thinking about sharding.

Doesn't fit: Multi-region writes with strong consistency. Anything beyond ~50GB. Full-text search at scale (use Vectorize or a dedicated service).

For Signal's newsletter list, D1 is comically over-spec'd. That's fine — it'll never break.

Continue reading