Search docs

Jump between documentation pages.

Browse docs

Use Cloudflare D1 with DaloyJS

Cloudflare D1 is a serverless SQLite-compatible database built into Cloudflare Workers. You access it through a Worker binding (no network driver, no auth token, no TCP), making it the lowest-friction database for the Cloudflare adapter.

1. Provision via Wrangler

ts
pnpm add -D wrangler
pnpm dlx wrangler d1 create my-app-db

Add the returned binding to your wrangler.toml:

ts
[[d1_databases]]
binding = "DB"
database_name = "my-app-db"
database_id = "<id-from-create>"

2. Type the binding

ts
// src/types/env.d.ts
export interface Env {
  DB: D1Database;
}

3. Decorate the app per-request

D1 bindings live on env, not process.env, so decorate inside the Worker's fetch handler and call app.fetch(req) directly:

Per-request binding flow
  1. 01Worker fetch(req, env)env.DB is the D1 binding
  2. 02app.decorate('db', env.DB)attach the binding to app state
  3. 03app.fetch(req)route handler reads state.db
  4. 04state.db.prepare(...).all()query D1, no network driver
D1 bindings live on env, not process.env, so decorate the app inside the Worker fetch handler on every request before calling app.fetch(req).
ts
import { z } from "zod";
import { App, secureHeaders } from "@daloyjs/core";
import type { Env } from "./types/env";

const app = new App();
app.use(secureHeaders());

const TodoSchema = z.object({ id: z.number(), title: z.string(), done: z.boolean() });

app.route({
  method: "GET",
  path: "/todos",
  operationId: "listTodos",
  responses: { 200: { description: "ok", body: z.array(TodoSchema) } },
  handler: async ({ state }) => {
    const { results } = await state.db
      .prepare("select id, title, done from todos")
      .all<{ id: number; title: string; done: number }>();
    return {
      status: 200,
      body: results.map((r) => ({ ...r, done: Boolean(r.done) })),
    };
  },
});

export default {
  async fetch(req: Request, env: Env) {
    app.decorate("db", env.DB);
    return app.fetch(req);
  },
};

4. Augment app state

ts
// src/types/state.d.ts
declare module "@daloyjs/core" {
  interface AppState {
    db: D1Database;
  }
}

Migrations

ts
pnpm dlx wrangler d1 migrations create my-app-db init
pnpm dlx wrangler d1 migrations apply my-app-db --local
pnpm dlx wrangler d1 migrations apply my-app-db --remote

With Drizzle ORM

ts
pnpm add drizzle-orm
// src/db/drizzle.ts
import { drizzle } from "drizzle-orm/d1";
import type { Env } from "../types/env";

export const createDb = (env: Env) => drizzle(env.DB);

With Prisma

Prisma supports D1 via the D1 Driver Adapter. Construct the adapter inside the Worker handler since it needs the runtime binding.

Limitations to know

  • D1 only runs in Cloudflare Workers, no Node.js, Lambda, or Edge runtime support.
  • Local development uses wrangler dev with a local SQLite file; behavior is close but not identical to production.
  • For multi-runtime portability, prefer Turso (libSQL) instead.

See also Turso, Neon, and the database hosting overview.