← All @molecule/* packages · App templates
@molecule/api-database-d1Provider 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-d1npm · Source on GitHub · Implements @molecule/api-database
@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
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.
@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.
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>"
provider
npm install @molecule/api-database-d1 @molecule/api-database @molecule/api-database-sqlite
D1ConfigConfiguration 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
}
D1DatabaseLikeThe 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[] }[]>
}
D1PreparedStatementLikeThe 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 }
}>
}
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.
Implements @molecule/api-database interface.
Peer dependencies:
@molecule/api-database ^1.0.1@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.