Drizzle ORM

Cloudflare D1 and Durable Objects

Declare tables with the SQLite package, then connect them to D1 or Durable Objects storage.

Cloudflare has two SQLite databases: D1 and the SQL storage inside a Durable Object. Both use the SQLite package.

npm install @mionjs/drizzle-orm-sqlite-core @mionjs/drizzle-orm @mionjs/run-types
npm install drizzle-orm   # only needed to run queries

There is no D1 or Durable Objects package: in Drizzle ORM, drizzle-orm/d1 and drizzle-orm/durable-sqlite are only drivers. The tables come from drizzle-orm/sqlite-core, which @mionjs/drizzle-orm-sqlite-core already covers. You declare a table once and pick a driver where you connect.

Connecting to D1

Pass the binding from your Worker's env to drizzle, and the table through toDrizzle.

import {drizzle} from 'drizzle-orm/d1';
import {eq} from 'drizzle-orm';
import {toDrizzle} from '@mionjs/drizzle-orm-sqlite-core/drizzle';
import {notes, type Note} from './drizzle-proxy-sqlite-example.ts';

// built once
const notesDb = toDrizzle(notes);

// the database binding
interface Env {
  DB: Parameters<typeof drizzle>[0];
}

export async function findNote(
  env: Env,
  id: number
): Promise<Note | undefined> {
  const db = drizzle(env.DB);
  // the timestamp is already a Date
  const [note] = await db.select().from(notesDb).where(eq(notesDb.id, id));
  return note;
}

Connecting to Durable Objects

Inside the object, pass its storage to drizzle instead. Everything else is identical.

import {drizzle} from 'drizzle-orm/durable-sqlite';
import type {DrizzleSqliteDODatabase} from 'drizzle-orm/durable-sqlite';
import {toDrizzle} from '@mionjs/drizzle-orm-sqlite-core/drizzle';
import {
  notes,
  type Note,
  type NewNote,
} from './drizzle-proxy-sqlite-example.ts';

const notesDb = toDrizzle(notes);

// storage needed by Drizzle
interface Storage {
  sql: {exec(query: string, ...bindings: unknown[]): unknown};
}

export class NotesObject {
  private readonly db: DrizzleSqliteDODatabase;

  constructor(storage: Storage) {
    this.db = drizzle(storage as never);
  }

  // models, validators and table work across databases
  async addNote(note: NewNote): Promise<Note> {
    const [row] = await this.db.insert(notesDb).values(note).returning();
    return row;
  }
}

Features on Cloudflare

It all comes from the table's types, as on any other database:

  • routes typed with the inferred models, validated before your handler runs
  • a timestamp column that arrives as a real Date, in both directions, with nothing to convert by hand
  • mock data and friendly error messages generated from the same models

Serve those routes with @mionjs/platform-cloudflare.

Copyright © 2026