For the complete documentation index, see llms.txt. Prefer markdown by appending.mdto documentation URLs or sendingAccept: text/markdown.
Overview
Use the Edge database client, understand local and remote D1 state, and choose the right command for migrations and development seeds.
Your application database is the DB binding. The app uses Drizzle's D1 adapter over Cloudflare's SQLite database. It's simpler and there is no database connection URL to configure, it just works.
Why D1 and Drizzle?
Database model
Cloudflare D1 is a managed SQL database built on SQLite. It stores structured application records, such as customer accounts, subscriptions, and the resources your product adds. Relationships connect those records, while constraints help preserve valid data.
Drizzle gives your TypeScript code a typed view of the database. You describe tables in a schema, review generated SQL migrations, and use queries to read or change records through the existing client.
| Concept | Role |
|---|---|
| Schema | The tables, columns, relationships, and constraints your code expects |
| Migration | A reviewed change applied to an existing database |
| Query | A read or write performed by an application feature |
| Seed | Development data for exercising the product locally |
Changing a schema definition alone does not change a running database. Migrations bring the database into agreement with that definition, while queries operate on the state currently deployed.
Keep file contents in R2 storage and their ownership or descriptive records in D1. Use KV for reusable values that tolerate its consistency model, rather than replacing relational product data with a cache.
Usage
In server code, import the existing client and use it to query the database:
import { eq } from "drizzle-orm";
import { db } from "@/db";
import { user } from "@/db/schema";
export async function findUser(userId: string) {
const [record] = await db.select().from(user).where(eq(user.id, userId));
return record ?? null;
}Call database helpers through a protected server function. Resolve the user ID from the session before filtering private records.
Local vs production
The Cloudflare Vite plugin simulates DB locally, storing state under .wrangler/state. Your deployed Worker uses the database ID in wrangler.jsonc. Applying a migration locally does not change remote D1, and production data is not automatically copied into your development replica.
While pnpm dev runs, open Cloudflare's local SQL Studio to browse D1 tables, inspect rows, and run queries against this replica.
| Task | Command | Target |
|---|---|---|
| Apply migrations and seed a dev user | pnpm db:setup | Local replica |
| Apply pending migrations only | pnpm db:migrate --local | Local replica |
| Seed the development user | pnpm db:seed | Local replica |
| Regenerate auth schema and generate SQL | pnpm db:generate | Schema files, not database data |
| Build, migrate, and deploy | pnpm deploy | Remote database, then Worker |
The seed script uses getPlatformProxy with remoteBindings: false. It does not seed production. The development user is verified and gets a password account, so you can sign in without waiting for email. SEED_EMAIL and SEED_PASSWORD control its credentials.
Running the seed again updates the existing user's profile, but does not replace an existing credential account's password. Changing SEED_PASSWORD is not a password reset. Use the app's password reset flow or recreate your disposable local user when testing different credentials.
Generated auth tables
pnpm db:generate overwrites src/db/schema/auth.ts with Better Auth's schema before generating SQL. Keep application tables in other files under src/db/schema/ and export them from src/db/schema/index.ts. Follow schema changes before adding your first table.
Schema
Add an application table beside the generated auth schema, export it from the schema barrel, and create a reviewed SQLite migration.
Queries
Application data with D1 and Drizzle: customer ownership, reads and writes, relationships, pagination, and query design for new features.
Migrations
Apply and inspect local D1 migrations, understand what the development seed changes, and deploy compatible schema changes to remote D1.
How is this guide?
Last updated on