@molecule/api-database-d1
Provider bond · database · API (Node) · v1.0.17 · 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-d1npm · 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
@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.
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.tsJSDoc, not this file.
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
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 }
}>
}
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[] }[]>
}
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
}
Functions
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'senv.
Returns: A DataStore backed by D1.
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'senv.
Returns: A DatabasePool 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 inprocess.env. SosetupBonds()takesenvon 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.transactionisundefinedrather than a no-op that reports success for a rollback which never happened. D1 offersbatch()— one atomic set of statements decided up front — which is a different shape, not a drop-in. Checktypeof 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 withwrangler d1 migrations applyfrom 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-sqlite3exposes 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.