Skip to content

D1

D1 is SQLite on env.<binding> — the same Worker API as Cloudflare D1 (within celld gaps).

celld runs queries and stores the database in the version fleet bucket. cellp imports or branches D1 when a version starts and exposes SQL operator APIs.

Bindings overview · Binding guides · Platform data

1. Declare it

In wrangler.jsonc:

jsonc
"d1_databases": [
  {
    "binding": "DB",
    "database_name": "commerce",
    "database_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  }
]

bindingenv.DB. Keep database_id unchanged on child versions — cellp inherits the parent id on fork, and a mismatched id breaks D1 branch (LTX scope). One d1_databases entry per bundle is the supported shape. Full file: Configure bindings.

2. Use it in the Worker

js
export default {
  async fetch(request, env) {
    await env.DB.exec(`
      CREATE TABLE IF NOT EXISTS products (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        price_cents INTEGER NOT NULL
      );
    `)

    if (request.method === 'POST') {
      const { name, price_cents } = await request.json()
      await env.DB.prepare(
        'insert into products (name, price_cents) values (?, ?)',
      )
        .bind(name, price_cents)
        .run()
      return new Response(null, { status: 201 })
    }

    const { results } = await env.DB.prepare(
      'select id, name, price_cents from products order by id',
    ).all()
    return Response.json({ products: results })
  },
}

Also useful: .first(), .first('column'), db.batch([...]) as celld implements them.

3. Put rows in (not via wrangler)

Wrangler only names the database. Data comes from:

MethodWhen
seed.db in the artifactRoot version import (CI or local seed scripts)
SQL in the WorkerSchema + small fixtures (CREATE TABLE IF NOT EXISTS)
Dashboard → Storage → D1Browse tables, run SQL on a ready version
Child versionAutomatic branch from parent_version_id (celld d1 branch into the child bucket)
Root + offshoot exportPlatform may seed from an offshoot export on first deploy—see Platform data

Step-by-step: Platform data.

Preview vs production

Version kindD1 data
Root (no parent)Empty unless you seed.db, SQL in the Worker, or Dashboard
Child (parent_version_id)Branch — parent snapshot at fork + writes in this version’s bucket
Preview HostReads/writes this version’s fork only
Production HostUnchanged until you promote the version that should serve prod

Promote switches which version’s bucket is production; it does not merge rows written on the old prod line after the fork.

Operator API (optional)

Same as the Dashboard, for scripts. Prefix:

/v1/projects/{project}/versions/{version}/

CallUse
GET …/databaseSummary
GET …/database/tablesTable list
GET …/database/tables/{name}/rowsBrowse (limit, offset)
POST …/database/query{ "sql": "…" }

503 → that version’s celld is down (archived or still deploying). Requires admin token — see API.

celld vs Cloudflare

  • Result sets cap at 100,000 rows or 32 MiB per query.
  • Invalid UTF-8 in SQLite TEXT is rejected — use BLOB for arbitrary bytes.

More: celld cloudflare-compat — D1.

Not included

Cloudflare D1 Time Travel, sharing one SQLite file across two ready versions, posting SQLite bytes in JSON.

Self-hosted Workers control plane. Not affiliated with Cloudflare or Vercel.