Skip to content

Database

Database integration with Cloudflare D1 (SQLite) and Drizzle ORM. Pre-configured for local development, migrations, and production deployment.

Key features

  • SQLite database running on Cloudflare’s edge
  • Type-safe queries with Drizzle ORM
  • Automatic migration generation
  • Visual database browser with Drizzle Studio
  • Separate databases for production and preview
Terminal window
bun run db:studio # Open visual database browser
bun run db:generate # Generate migrations from schema changes
bun run db:migrate # Apply migrations locally
bun run db:reset # Delete all migrations and regenerate from scratch

Use getMainDb() from @/db/main to access the Drizzle ORM instance. Call it inside server functions:

import { createServerFn } from "@tanstack/react-start"
import { getMainDb } from "@/db/main"
import { users } from "@/db/main/schema"
const listUsers = createServerFn({ method: "GET" }).handler(async () => {
const db = getMainDb()
return db.select().from(users).all()
})

The db object is a Drizzle ORM instance with full TypeScript support.

Database schemas are defined in feature modules and then aggregated at packages/db/main/:

  • Directoryapps/
    • user/db/schema.ts User tables
    • permissions/db/schema.ts Permission tables
    • payments/db/schema.ts Payment tables
  • Directorypackages/db/
    • Directorymain/
      • schema.ts Main database schema
      • Directorymigrations/ Generated SQL migrations

Example schema:

import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
export const users = sqliteTable("users", {
id: integer("id").primaryKey(),
email: text("email").notNull(),
})
  1. Create or update a schema file in your feature’s db/ folder:

    apps/blog/db/schema.ts
    import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
    export const posts = sqliteTable("posts", {
    id: integer("id").primaryKey(),
    title: text("title").notNull(),
    content: text("content"),
    })
  2. Export it from packages/db/main/schema.ts:

    export * from "@/blog/db/schema"
  3. Generate a migration:

    Terminal window
    bun run db:generate
  4. Apply the migration locally:

    Terminal window
    bun run db:migrate

The migration is automatically applied in CI/CD via Wrangler when you push your changes.

The database package is structured to support multiple D1 databases. Each database configuration entry point lives under its own subdirectory in packages/db/, named after the binding (minus the DB_ prefix and lowercased). The main database is at packages/db/main/ - corresponding to the DB_MAIN binding - but additional databases can be added following the same pattern, e.g. packages/db/secondary/ for a DB_SECONDARY binding.

  1. Create the new database in Cloudflare:

    Terminal window
    bunx wrangler d1 create <project-name>-secondary-development
  2. Register the new binding in wrangler.json for every environment:

    "d1_databases": [
    { "binding": "DB_MAIN", ... },
    {
    "binding": "DB_SECONDARY",
    "database_name": "<project-name>-secondary-development",
    "database_id": "<DATABASE_ID>",
    "migrations_dir": "./packages/db/secondary/migrations"
    }
    ]
  3. Create the schema and db server files:

    packages/db/secondary/schema.ts
    import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
    export const posts = sqliteTable("posts", {
    id: integer("id").primaryKey(),
    title: text("title").notNull(),
    })
    packages/db/secondary/db.server.ts
    import { env } from "cloudflare:workers"
    import { drizzle } from "drizzle-orm/d1"
    import * as schema from "./schema"
    export function getSecondaryDb() {
    return drizzle(env.DB_SECONDARY, { schema })
    }
  4. Generate and apply migrations for the new database:

    Terminal window
    DB_BINDING=DB_SECONDARY bun run db:generate
    DB_BINDING=DB_SECONDARY bun run db:migrate

Browse and edit your database visually:

Terminal window
bun run db:studio

Opens at local.drizzle.studio where you can:

  • View all tables and their data
  • Run queries
  • Edit records directly
  • Explore relationships

Drizzle Studio