Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/iterate/sqlfu/llms.txt

Use this file to discover all available pages before exploring further.

sqlfu can talk to a deployed Cloudflare D1 database directly, so migrate, check, sync, goto, baseline, and the UI all operate on the real cloud database your Worker uses — not a separate local SQLite file. The helpers in sqlfu/cloudflare make that connection one line of config.

When to use this

Alchemy v2 dropped Miniflare D1 emulation on purpose. alchemy dev provisions real cloud resources; only your Worker code runs locally. There is no local Miniflare SQLite file for findMiniflareD1Path to find.Use createAlchemyD1Client to point sqlfu at the deployed D1 database your Worker already uses.
If you only want sqlfu to author migrations and generate typed wrappers, you can omit db entirely. Commands that need a database use the local .sqlfu/app.db file. Add a db factory only when you want migrate, check, sync, or the UI to operate on the real D1 database.

One-line recipe with createAlchemyD1Client

If you are on Alchemy v2 and your alchemy.run.ts declares a Cloudflare.D1Database("database") resource, createAlchemyD1Client reads alchemy’s local state and produces a sqlfu db factory in one call:
sqlfu.config.ts
import {defineConfig} from 'sqlfu';
import {createAlchemyD1Client} from 'sqlfu/cloudflare';

export default defineConfig({
  db: () => createAlchemyD1Client({stack: 'my-app', stage: 'dev', fqn: 'database'}),
  migrations: {path: './migrations', preset: 'd1'},
  definitions: './definitions.sql',
  queries: './sql',
});
apiToken falls back to process.env.CLOUDFLARE_API_TOKEN. accountId and databaseId come from the alchemy state file at .alchemy/state/<stack>/<stage>/<fqn>.json — no UUID copy-paste required.

Compose your own factory

sqlfu/cloudflare is four small helpers. createAlchemyD1Client is one valid composition. If your project needs a different state location, dynamic database resolution, or a custom auth source, wire your own factory from the parts.

With alchemy state

readAlchemyD1State reads alchemy v2’s local state file and returns the deployed resource’s identity. Hand it to createD1HttpClient for a sqlfu db factory:
sqlfu.config.ts
import {defineConfig} from 'sqlfu';
import {createD1HttpClient, readAlchemyD1State} from 'sqlfu/cloudflare';

export default defineConfig({
  db: () => {
    const {databaseId, accountId} = readAlchemyD1State({
      stack: 'my-app', stage: 'dev', fqn: 'database',
    });
    return {
      client: createD1HttpClient({
        accountId,
        databaseId,
        apiToken: process.env.CLOUDFLARE_API_TOKEN!,
      }),
    };
  },
  migrations: {path: './migrations', preset: 'd1'},
});
State lives at .alchemy/state/<stack>/<stage>/<encoded-fqn>.json. readAlchemyD1State walks up from the cwd until it finds an .alchemy/state/ directory, so it works from any subdirectory of your project. Pass {alchemyDir: '/abs/path/to/.alchemy'} to override. The fqn is the resource’s fully-qualified name inside the alchemy app. For a top-level Cloudflare.D1Database("database") the fqn is just "database". For a D1Database("database") nested inside Namespace("Auth").run(...), it is "Auth/database".
readAlchemyD1State is alchemy v2 specific. Alchemy v1 stored state at a different path and in a different shape. If you are on v1, use findCloudflareD1ByName instead.

Without alchemy

If you are not on alchemy — or prefer not to have sqlfu read alchemy state files — point at the deployed D1 with credentials and either an explicit databaseId or a name lookup:
sqlfu.config.ts
import {defineConfig} from 'sqlfu';
import {createD1HttpClient, findCloudflareD1ByName} from 'sqlfu/cloudflare';

export default defineConfig({
  db: async () => {
    const accountId = process.env.CLOUDFLARE_ACCOUNT_ID!;
    const apiToken = process.env.CLOUDFLARE_API_TOKEN!;
    const {databaseId} = await findCloudflareD1ByName({
      accountId, apiToken, name: 'my-app-prod-database',
    });
    return {client: createD1HttpClient({accountId, apiToken, databaseId})};
  },
  migrations: {path: './migrations', preset: 'd1'},
});
If you already have a databaseId — from an env var, a hardcoded UUID, or any other source — skip the lookup and pass it straight to createD1HttpClient. findCloudflareD1ByName uses GET /accounts/{id}/d1/database?name=... and throws on zero or multiple matches.
Use findCloudflareD1ByName when your config serves multiple environments by reading process.env.STAGE, or when you are on Alchemy v1 and want to avoid depending on its on-disk state format.

Migration preset

D1 projects that were already using alchemy or wrangler have a d1_migrations table. Set preset: 'd1' so sqlfu reads and writes that same table:
migrations: {path: './migrations', preset: 'd1'},
Under preset: 'd1', sqlfu uses the d1_migrations table, four-digit filename prefixes by default, and alchemy-compatible id sequencing. See Migration presets for the full schema and checksum tradeoff.

Worker runtime

At Worker runtime, use createD1Client with the D1 binding from env. This is separate from the config-time HTTP client — it is the adapter for queries inside your Worker:
src/worker.ts
import {createD1Client} from 'sqlfu';
import {listPublishedPosts} from './db/queries/.generated/queries.sql.ts';

type Env = {DB: D1Database};

export default {
  async fetch(_request: Request, env: Env) {
    const client = createD1Client(env.DB);
    const posts = await listPublishedPosts(client, {limit: 20});
    return Response.json(posts);
  },
};
createD1Client wraps the D1Database binding and returns an AsyncClient. Generated wrappers do not know or care that the runtime is D1 — they only need a sqlfu AsyncClient.

What is not included

  • Local D1 emulation. Alchemy v2 explicitly avoids this. If you want a local Miniflare SQLite, run wrangler dev and point sqlfu at the wrangler-managed file via findMiniflareD1Path (works for both alchemy v1 and wrangler persist layouts).
  • A bundled Cloudflare SDK. createD1HttpClient uses raw fetch. No dependency on cloudflare or wrangler.
  • Credential discovery. The helpers do not read ~/.wrangler/config/default.toml or launch an OAuth flow. Pass an API token explicitly or via CLOUDFLARE_API_TOKEN.

Cloudflare Alchemy

Alchemy v1 vs v2, findMiniflareD1Path, and state file details.

Durable Objects

Per-instance SQLite with synchronous migrations at startup.

Migration presets

The d1 preset, table schema, and checksum tradeoff.

Adapters reference

Full driver table and copy-paste snippets for every adapter.

Build docs developers (and LLMs) love