← All @molecule/* packages · App templates

@molecule/api-database-d1

Provider bond · database · API (Node) · v1.0.3 · Apache-2.0

Database provider for Cloudflare D1 — SQLite on Workers, reusing the SQLite dialect with a D1-backed pool instead of a native driver.

npm install @molecule/api-database-d1

npm · Source on GitHub · Implements @molecule/api-database

How it works

@molecule/api-database-d1 is a provider bond on the API (Node) side: it implements the database core interface (@molecule/api-database) with a concrete vendor or library behind it.

Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.

import { setStore } from '@molecule/api-database'
import { createProvider, type D1DatabaseLike } from '@molecule/api-database-d1'

// Worker bindings arrive per-invocation on `env`; they are not in module
// scope and not in process.env, so setupBonds() takes `env` on Workers.
// (Here `env` stands in for the real `scheduled(event, env, ctx)` argument.)
const env = { DB: {} as D1DatabaseLike }

setStore(createProvider({ database: env.DB }))

// wrangler.toml:
//   [[d1_databases]]
//   binding = "DB"
//   database_name = "my-app"
//   database_id = "<id>"

Works with: @molecule/api-database

Reference

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

@molecule/api-database-d1 — a @molecule/api-database provider for Cloudflare D1, so a molecule API can run on Workers with no Postgres.

D1 is SQLite, so this bond reuses @molecule/api-database-sqlite's dialect (query building, placeholder conversion, id generation) verbatim and replaces only the pool: better-sqlite3 is a synchronous native binding, D1 is an async platform binding. The native driver is never imported, which is what lets this run in a Workers isolate at all.

Quick Start

import { setStore } from '@molecule/api-database'
import { createProvider, type D1DatabaseLike } from '@molecule/api-database-d1'

// Worker bindings arrive per-invocation on `env`; they are not in module
// scope and not in process.env, so setupBonds() takes `env` on Workers.
// (Here `env` stands in for the real `scheduled(event, env, ctx)` argument.)
const env = { DB: {} as D1DatabaseLike }

setStore(createProvider({ database: env.DB }))

// wrangler.toml:
//   [[d1_databases]]
//   binding = "DB"
//   database_name = "my-app"
//   database_id = "<id>"

Type

provider

Installation

npm install @molecule/api-database-d1 @molecule/api-database @molecule/api-database-sqlite

API

Interfaces

D1Config

Configuration for the D1 provider.

interface D1Config {
  /**
   * The D1 binding from the Worker's `env` (for example `env.DB`).
   *
   * REQUIRED and passed in explicitly: a Worker's bindings arrive per-invocation
   * on `env` and are not readable from the module scope or from `process.env`,
   * so there is nothing for this bond to discover. Being told is the only
   * correct option, and a discovery mechanism here could only ever guess wrong.
   */
  database: D1DatabaseLike
}

D1DatabaseLike

The subset of Cloudflare's D1Database binding this bond uses.

interface D1DatabaseLike {
  /** Prepares a SQL statement. */
  prepare(query: string): D1PreparedStatementLike
  /** Runs a set of prepared statements as one batch. */
  batch?<T = Record<string, unknown>>(
    statements: D1PreparedStatementLike[],
  ): Promise<{ results: T[] }[]>
}

D1PreparedStatementLike

The subset of Cloudflare's D1PreparedStatement this bond uses.

Declared structurally rather than imported from @cloudflare/workers-types so the package carries no dependency on the Workers type package — a consumer that already has those types passes its real binding and it type-checks, and a consumer that does not can still build.

interface D1PreparedStatementLike {
  /** Binds ordinal parameters, returning a bound statement. */
  bind(...values: unknown[]): D1PreparedStatementLike
  /** Runs the statement and returns all result rows plus metadata. */
  all<T = Record<string, unknown>>(): Promise<{
    results: T[]
    meta?: { changes?: number; last_row_id?: number | string }
  }>
  /** Runs the statement for its side effects. */
  run(): Promise<{
    results?: unknown[]
    meta?: { changes?: number; last_row_id?: number | string }
  }>
}

Functions

createDatabasePool(config)

Creates the D1 pool on its own, for callers that want raw SQL access alongside the DataStore.

function createDatabasePool(config: D1Config): DatabasePool
  • config — The D1 binding from the Worker's env.

Returns: A DatabasePool backed by D1.

createPool(config)

Creates a DatabasePool over a Cloudflare D1 binding.

function createPool(config: D1Config): DatabasePool
  • config — The D1 binding to use.

Returns: A pool the shared SQLite store can run against.

createProvider(config)

Creates a D1-backed DataStore.

The store is @molecule/api-database-sqlite's, unchanged: D1 speaks SQLite, so the dialect is shared and only the pool differs. That is why this bond is small — the sqlite store was already written against the abstract DatabasePool rather than against its native driver.

function createProvider(config: D1Config): DataStore
  • config — The D1 binding from the Worker's env.

Returns: A DataStore backed by D1.

Core Interface

Implements @molecule/api-database interface.

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-database ^1.0.1

Runtime Dependencies

  • @molecule/api-database

  • @molecule/api-database-sqlite

  • The binding must be PASSED IN; it cannot be discovered. A Worker's bindings arrive per-invocation on env — they are not in the module scope and not in process.env. So setupBonds() takes env on Workers, and the provider is constructed per invocation rather than once at import time.

  • There are no interactive transactions, and this bond does not fake one. pool.transaction is undefined rather than a no-op that reports success for a rollback which never happened. D1 offers batch() — one atomic set of statements decided up front — which is a different shape, not a drop-in. Check typeof pool.transaction === 'function' before relying on it.

  • Migrations do not run through this bond. @molecule/api-database-sqlite's migrator reads the filesystem and opens the native driver, neither of which exists on Workers. Apply schema with wrangler d1 migrations apply from CI or a local shell, the same way you would run any other out-of-band migration.

  • D1 rows come back as plain JSON values. There is no per-column type metadata the way better-sqlite3 exposes it, so a column's declared type cannot be used to re-hydrate values; store dates as ISO strings and booleans as 0/1, which is what the shared SQLite dialect already writes.