Skip to content

Instantly share code, notes, and snippets.

@flopex
Last active July 24, 2025 21:11
Show Gist options
  • Select an option

  • Save flopex/8ba626b2dc650947882d3f45769c4702 to your computer and use it in GitHub Desktop.

Select an option

Save flopex/8ba626b2dc650947882d3f45769c4702 to your computer and use it in GitHub Desktop.
Cloudflare D1 through drizzle-orm without the need/use of Cloudflare Worker binding
// index.ts

import { sql } from 'drizzle-orm';
import { drizzle } from 'drizzle-orm/sqlite-proxy';
import type { AsyncRemoteCallback } from 'drizzle-orm/sqlite-proxy';
import { d1HttpDriver } from './d1-http-driver';
import * as schema from './schema';

const wrappedDriver: AsyncRemoteCallback = async (sql: string, params: unknown[], method: "all" | "run" | "get" | "values") => {
  if (method === "values") {
    const result = await d1HttpDriver(sql, params, "all");
    return result;
  }
  return d1HttpDriver(sql, params, method);
};

const db = drizzle(wrappedDriver, { schema });

export default db;

export type DrizzleClient = typeof db;
// d1-http-driver.ts

import ky from 'ky';

const { CLOUDFLARE_D1_TOKEN, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_DATABASE_ID } = process.env;

const D1_API_BASE_URL = `https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/d1/database/${CLOUDFLARE_DATABASE_ID}`

const d1HttpClient = ky.create({
        prefixUrl: D1_API_BASE_URL,
	headers: {
		Authorization: `Bearer ${CLOUDFLARE_D1_TOKEN}`,
	},
});

type AsyncRemoteCallback = (
  sql: string,
  params: unknown[],
  method: 'all' | 'run' | 'get' | 'values'
) => Promise<{ rows: any }>;

export const d1HttpDriver = async (
    sql: string,
    params: unknown[],
    method: 'all' | 'run' | 'get'
) => {
	const res = await d1HttpClient.post('query', {
		json: { sql, params, method },
	});

	const data = (await res.json()) as Record<string, any>;

	if (data.errors.length > 0 || !data.success) {
		throw new Error(
			`Error from sqlite proxy server: \n${JSON.stringify(data)}}`
		);
	}

	const qResult = data.result[0];

	if (!qResult.success) {
		throw new Error(
			`Error from sqlite proxy server: \n${JSON.stringify(data)}`
		);
	}

	// https://orm.drizzle.team/docs/get-started-sqlite#http-proxy
	return { rows: qResult.results.map((r: any) => Object.values(r)) };
};
// drizzle.config.ts

import dotenv from 'dotenv';

import { defineConfig } from 'drizzle-kit';
import type { Config } from 'drizzle-kit';

dotenv.config({ path: '.env.local' });

export default defineConfig({
  dialect: 'sqlite',
  schema: './db/schema.ts',
  out: './db/migrations',
  driver: 'd1-http',
  dbCredentials: {
    accountId: process.env.CLOUDFLARE_ACCOUNT_ID!,
    databaseId: process.env.CLOUDFLARE_DATABASE_ID!,
    token: process.env.CLOUDFLARE_D1_TOKEN!,
  },
}) satisfies Config;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment