@pitlane/data-table-d1
A Cloudflare D1 driver for Remix 3's data-table.
D1 is SQLite, so the SQL is SQLite's, but the execution model is not: statements are prepared and awaited over an RPC binding, and the transaction verbs are rejected outright. @remix-run/data-table-sqlite builds on a synchronous client and cannot bridge that gap, so this package pairs the SQLite SQL compiler with a driver written against D1's async API.
Classes
D1Database
A Database bound to Cloudflare D1.
The same shape SqliteDatabase and PostgresDatabase have: a Database subclass that supplies its own driver, so every query, persistence and migration method comes from remix/data-table unchanged.
Extends
Database<"sqlite">
Constructors
Constructor
new D1Database(binding, options?): D1Database;Parameters
binding
options?
Returns
Overrides
Database<"sqlite">.constructorAccessors
capabilities
Get Signature
get capabilities(): DatabaseCapabilities;Immutable feature flags used by shared query and migration behavior.
Returns
DatabaseCapabilities
Inherited from
Database.capabilitiesdialect
Get Signature
get dialect(): dialect;Stable identifier for the SQL dialect.
Returns
dialect
Inherited from
Database.dialectMethods
batch()
batch(statements): Promise<D1BatchResult[]>;Runs statements together, atomically.
D1 has no transactions, so transaction() refuses. batch() is what it offers instead, and this is it without reaching for the raw binding:
import { sql } from "remix/data-table";
await db.batch([
sql`insert into post (title) values (${title})`,
sql`update counter set posts = posts + 1`,
]);The statements are SqlStatements rather than query-builder calls, because data-table exposes no way to build an operation without running it. sql still parameterises the values.
Parameters
statements
SqlStatement[]
Returns
Promise<D1BatchResult[]>
close()
close(): Promise<void>;Closes resources owned by this database.
Returns
Promise<void>
A promise that resolves when owned resources have been released.
Inherited from
Database.closecount()
count<table>(table, options?): Promise<number>;Type Parameters
table
table extends AnyTable
Parameters
table
table
options?
CountOptions<table>
Returns
Promise<number>
Inherited from
Database.countcreate()
Call Signature
create<table>(
table,
values,
options?): Promise<WriteResult>;Type Parameters
table
table extends AnyTable
Parameters
table
table
values
Partial<TableRow<table>>
options?
CreateResultOptions
Returns
Promise<WriteResult>
Inherited from
Database.createCall Signature
create<table, relations>(
table,
values,
options): Promise<{ [key in string | number | symbol]: ({ [key in string]: { [column in string]: ColumnOutput<TableColumns<table>[column]> }[key] } & { [key in string | number | symbol]: { [name in string | number | symbol]: RelationResult<relations[name]> }[key] })[key] }>;Type Parameters
table
table extends AnyTable
relations
relations extends RelationMapForSourceName<TableName<table>> = { }
Parameters
table
table
values
Partial<TableRow<table>>
options
CreateRowOptions<table, relations>
Returns
Promise<{ [key in string | number | symbol]: ({ [key in string]: { [column in string]: ColumnOutput<TableColumns<table>[column]> }[key] } & { [key in string | number | symbol]: { [name in string | number | symbol]: RelationResult<relations[name]> }[key] })[key] }>
Inherited from
Database.createcreateMany()
Call Signature
createMany<table>(
table,
values,
options?): Promise<WriteResult>;Type Parameters
table
table extends AnyTable
Parameters
table
table
values
Partial<{ [key in string]: { [column in string]: ColumnOutput<TableColumns<table>[column]> }[key] }>[]
options?
CreateManyResultOptions
Returns
Promise<WriteResult>
Inherited from
Database.createManyCall Signature
createMany<table>(
table,
values,
options): Promise<{ [key in string]: { [column in string]: ColumnOutput<TableColumns<table>[column]> }[key] }[]>;Type Parameters
table
table extends AnyTable
Parameters
table
table
values
Partial<{ [key in string]: { [column in string]: ColumnOutput<TableColumns<table>[column]> }[key] }>[]
options
CreateManyRowsOptions
Returns
Promise<{ [key in string]: { [column in string]: ColumnOutput<TableColumns<table>[column]> }[key] }[]>
Inherited from
Database.createManydelete()
delete<table>(table, value): Promise<boolean>;Type Parameters
table
table extends AnyTable
Parameters
table
table
value
PrimaryKeyInput<table>
Returns
Promise<boolean>
Inherited from
Database.deletedeleteMany()
deleteMany<table>(table, options): Promise<WriteResult>;Type Parameters
table
table extends AnyTable
Parameters
table
table
options
DeleteManyOptions<table>
Returns
Promise<WriteResult>
Inherited from
Database.deleteManyexec()
Call Signature
exec(statement, values?): Promise<DataManipulationResult>;Parameters
statement
string | SqlStatement
values?
unknown[]
Returns
Promise<DataManipulationResult>
Inherited from
Database.execCall Signature
exec<input>(input): Promise<QueryExecutionResult<input>>;Type Parameters
input
input extends AnyQuery
Parameters
input
input
Returns
Promise<QueryExecutionResult<input>>
Inherited from
Database.execexecuteScript()
executeScript(sql): Promise<void>;Executes a migration or raw multi-statement SQL script.
Parameters
sql
string
SQL script to execute.
Returns
Promise<void>
A promise that resolves when execution completes.
Inherited from
Database.executeScriptfind()
find<table, relations>(
table,
value,
options?): Promise<
| { [key in string | number | symbol]: ({ [key in string]: { [column in string]: ColumnOutput<TableColumns<(...)>[column]> }[key] } & { [key in string | number | symbol]: { [name in string | number | symbol]: RelationResult<relations[name]> }[key] })[key] }
| null>;Type Parameters
table
table extends AnyTable
relations
relations extends RelationMapForSourceName<TableName<table>> = { }
Parameters
table
table
value
PrimaryKeyInput<table>
options?
with?
relations
Returns
Promise< | { [key in string | number | symbol]: ({ [key in string]: { [column in string]: ColumnOutput<TableColumns<(...)>[column]> }[key] } & { [key in string | number | symbol]: { [name in string | number | symbol]: RelationResult<relations[name]> }[key] })[key] } | null>
Inherited from
Database.findfindMany()
findMany<table, relations>(table, options?): Promise<{ [key in string | number | symbol]: ({ [key in string]: { [column in string]: ColumnOutput<TableColumns<(...)>[column]> }[key] } & { [key in string | number | symbol]: { [name in string | number | symbol]: RelationResult<relations[name]> }[key] })[key] }[]>;Type Parameters
table
table extends AnyTable
relations
relations extends RelationMapForSourceName<TableName<table>> = { }
Parameters
table
table
options?
FindManyOptions<table, relations>
Returns
Promise<{ [key in string | number | symbol]: ({ [key in string]: { [column in string]: ColumnOutput<TableColumns<(...)>[column]> }[key] } & { [key in string | number | symbol]: { [name in string | number | symbol]: RelationResult<relations[name]> }[key] })[key] }[]>
Inherited from
Database.findManyfindOne()
findOne<table, relations>(table, options): Promise<
| { [key in string | number | symbol]: ({ [key in string]: { [column in string]: ColumnOutput<TableColumns<(...)>[column]> }[key] } & { [key in string | number | symbol]: { [name in string | number | symbol]: RelationResult<relations[name]> }[key] })[key] }
| null>;Type Parameters
table
table extends AnyTable
relations
relations extends RelationMapForSourceName<TableName<table>> = { }
Parameters
table
table
options
FindOneOptions<table, relations>
Returns
Promise< | { [key in string | number | symbol]: ({ [key in string]: { [column in string]: ColumnOutput<TableColumns<(...)>[column]> }[key] } & { [key in string | number | symbol]: { [name in string | number | symbol]: RelationResult<relations[name]> }[key] })[key] } | null>
Inherited from
Database.findOnehasColumn()
hasColumn(table, column): Promise<boolean>;Reports whether a column exists on a table.
Parameters
table
TableRef
Table to inspect.
column
string
Column name to inspect.
Returns
Promise<boolean>
A promise that resolves to true when the column exists.
Inherited from
Database.hasColumnhasTable()
hasTable(table): Promise<boolean>;Reports whether a table exists.
Parameters
table
TableRef
Table to inspect.
Returns
Promise<boolean>
A promise that resolves to true when the table exists.
Inherited from
Database.hasTablemigrate()
migrate(migrations, options?): Promise<MigrateResult>;Applies or reverts migrations in order.
Parameters
migrations
Migrations
Migration descriptors or registry to apply.
options?
DatabaseMigrateOptions
Migration direction, bound, dry-run, and journal configuration.
Returns
Promise<MigrateResult>
The migrations applied or reverted by this run and their SQL scripts.
Inherited from
Database.migratemigrationStatus()
migrationStatus(migrations, options?): Promise<MigrationStatusEntry[]>;Reports the current state of the provided migrations.
Parameters
migrations
Migrations
Migration descriptors or registry to inspect.
options?
DatabaseMigrationStatusOptions
Migration journal configuration.
Returns
Promise<MigrationStatusEntry[]>
Status entries for the provided migrations.
Inherited from
Database.migrationStatusnow()
now(): unknown;Returns
unknown
Inherited from
Database.nowquery()
query<tableName, row, primaryKey>(table): Query<QueryTableInput<tableName, row, primaryKey>, { [key in string]: QueryColumnTypeMapFromRow<tableName, row>[key] }, row, {
}, BoundQueryPhase<"all">>;Type Parameters
tableName
tableName extends string
row
row extends Record<string, unknown>
primaryKey
primaryKey extends readonly keyof row & string[]
Parameters
table
QueryTableInput<tableName, row, primaryKey>
Returns
Query<QueryTableInput<tableName, row, primaryKey>, { [key in string]: QueryColumnTypeMapFromRow<tableName, row>[key] }, row, { }, BoundQueryPhase<"all">>
Inherited from
Database.queryreset()
reset(options): Promise<void>;Wipes the database, applies migrations, and optionally seeds data.
Parameters
options
DatabaseResetOptions
Migrations and optional seed function used to rebuild the database.
Returns
Promise<void>
A promise that resolves when the database has been rebuilt.
Inherited from
Database.resettransaction()
transaction<result>(callback, options?): Promise<result>;Type Parameters
result
result
Parameters
callback
(database) => Promise<result>
options?
TransactionOptions
Returns
Promise<result>
Inherited from
Database.transactionupdate()
update<table, relations>(
table,
value,
changes,
options?): Promise<{ [key in string | number | symbol]: ({ [key in string]: { [column in string]: ColumnOutput<TableColumns<table>[column]> }[key] } & { [key in string | number | symbol]: { [name in string | number | symbol]: RelationResult<relations[name]> }[key] })[key] }>;Type Parameters
table
table extends AnyTable
relations
relations extends RelationMapForSourceName<TableName<table>> = { }
Parameters
table
table
value
PrimaryKeyInput<table>
changes
Partial<TableRow<table>>
options?
UpdateOptions<table, relations>
Returns
Promise<{ [key in string | number | symbol]: ({ [key in string]: { [column in string]: ColumnOutput<TableColumns<table>[column]> }[key] } & { [key in string | number | symbol]: { [name in string | number | symbol]: RelationResult<relations[name]> }[key] })[key] }>
Inherited from
Database.updateupdateMany()
updateMany<table>(
table,
changes,
options): Promise<WriteResult>;Type Parameters
table
table extends AnyTable
Parameters
table
table
changes
Partial<TableRow<table>>
options
UpdateManyOptions<table>
Returns
Promise<WriteResult>
Inherited from
Database.updateManywipe()
wipe(): Promise<void>;Destructively recreates the configured database.
Returns
Promise<void>
A promise that resolves when the database is ready for use.
Inherited from
Database.wipeD1DatabaseDriver
A DatabaseDriver backed by a Cloudflare D1 binding.
Statements are compiled by the same SQLite compiler @remix-run/data-table uses, then executed through D1's async prepared-statement API. Pass it to Database, or use createD1Database to get one already wired.
Implements
DatabaseDriver<"sqlite">
Constructors
Constructor
new D1DatabaseDriver(d1, options?): D1DatabaseDriver;Parameters
d1
options?
D1DriverOptions = {}
Returns
Accessors
capabilities
Get Signature
get capabilities(): Readonly<{
migrationLock: false;
returning: true;
savepoints: false;
transactionalDdl: false;
upsert: true;
}>;Immutable feature flags used by shared query and migration behavior.
Returns
Readonly<{ migrationLock: false; returning: true; savepoints: false; transactionalDdl: false; upsert: true; }>
Implementation of
DatabaseDriver.capabilitiesdialect
Get Signature
get dialect(): "sqlite";Stable identifier for the SQL dialect.
Returns
"sqlite"
Implementation of
DatabaseDriver.dialectMethods
batch()
batch(statements): Promise<D1BatchResult[]>;Runs statements together, atomically, through D1's batch().
This is the answer to "several writes must land together" on a database with no transactions. batch() is D1's only atomic primitive: it takes every statement up front and commits them as a unit, which is why it cannot back transaction() but can back this.
Statements are SqlStatements, so sql from remix/data-table parameterises them and there is no reaching for the raw binding:
await db.batch([
sql`insert into post (title) values (${title})`,
sql`update counter set posts = posts + 1`,
]);Parameters
statements
SqlStatement[]
The statements to run, in order.
Returns
Promise<D1BatchResult[]>
One result per statement, in the same order.
beginTransaction()
beginTransaction(_options?): Promise<TransactionToken>;Opens a transaction, if the caller accepted that it will not be one.
No BEGIN is sent, because D1 rejects it. The token exists so Database has something to carry; statements inside the scope run and commit exactly as they would outside it.
Parameters
_options?
TransactionOptions
Returns
Promise<TransactionToken>
Implementation of
DatabaseDriver.beginTransactionclose()
close(): void;A binding is owned by the runtime; there is no connection to release.
Returns
void
Implementation of
DatabaseDriver.closecommitTransaction()
commitTransaction(_token): Promise<void>;Nothing to commit: every statement in the scope already did.
Parameters
_token
TransactionToken
Returns
Promise<void>
Implementation of
DatabaseDriver.commitTransactioncreateSavepoint()
createSavepoint(_token, _name): Promise<void>;Creates a savepoint inside an open transaction.
Parameters
_token
TransactionToken
_name
string
Returns
Promise<void>
Implementation of
DatabaseDriver.createSavepointexecute()
execute(request): Promise<DataManipulationResult>;Executes a data-manipulation request.
Parameters
request
DataManipulationRequest
Returns
Promise<DataManipulationResult>
Implementation of
DatabaseDriver.executeexecuteScript()
executeScript(sql, _transaction?): Promise<void>;Executes a raw SQL script that may contain multiple statements.
Parameters
sql
string
_transaction?
TransactionToken
Returns
Promise<void>
Implementation of
DatabaseDriver.executeScripthasColumn()
hasColumn(
table,
column,
_transaction?): Promise<boolean>;Checks whether a column exists on a table.
Parameters
table
TableRef
column
string
_transaction?
TransactionToken
Returns
Promise<boolean>
Implementation of
DatabaseDriver.hasColumnhasTable()
hasTable(table, _transaction?): Promise<boolean>;Checks whether a table exists.
Parameters
table
TableRef
_transaction?
TransactionToken
Returns
Promise<boolean>
Implementation of
DatabaseDriver.hasTablereleaseSavepoint()
releaseSavepoint(_token, _name): Promise<void>;Releases a previously created savepoint.
Parameters
_token
TransactionToken
_name
string
Returns
Promise<void>
Implementation of
DatabaseDriver.releaseSavepointrollbackToSavepoint()
rollbackToSavepoint(_token, _name): Promise<void>;Rolls back to a previously created savepoint.
Parameters
_token
TransactionToken
_name
string
Returns
Promise<void>
Implementation of
DatabaseDriver.rollbackToSavepointrollbackTransaction()
rollbackTransaction(_token): Promise<void>;Nothing to roll back. This is the cost of unsafe-nonatomic, and it is silent by necessity: Database calls this while unwinding a failed callback, and throwing here would replace the caller's error with an AggregateError about a rollback that was never possible.
Parameters
_token
TransactionToken
Returns
Promise<void>
Implementation of
DatabaseDriver.rollbackTransactionwipe()
wipe(): Promise<void>;Drops every table the application owns.
D1 keeps its own bookkeeping in _cf_* tables and SQLite keeps sqlite_*; dropping either breaks the binding, so both are left alone. There is no file to delete the way the SQLite driver deletes one.
Returns
Promise<void>
Implementation of
DatabaseDriver.wipeInterfaces
D1BatchResult
What one statement in a D1DatabaseDriver.batch produced.
Properties
affectedRows
affectedRows: number;insertId
insertId: number;rows
rows: Record<string, unknown>[];Rows the statement returned. Empty for a write with no returning.
D1Binding
The slice of Cloudflare's D1 API this driver uses.
Declared structurally rather than imported from @cloudflare/workers-types, so the package adds no dependency and no ambient global types to a consumer that does not already have them. A real D1Database binding satisfies it; so does a test double.
Methods
batch()
batch(statements): Promise<D1Result[]>;Parameters
statements
Returns
Promise<D1Result[]>
exec()
exec(query): Promise<unknown>;Parameters
query
string
Returns
Promise<unknown>
prepare()
prepare(query): D1PreparedStatement;Parameters
query
string
Returns
D1DatabaseOptions
Extends
DatabaseOptions
Properties
now?
optional now?: () => unknown;Clock function used for auto-managed timestamps.
Returns
unknown
Inherited from
DatabaseOptions.nowonStatement?
optional onStatement?: D1StatementObserver;Called after each statement, with the rows read, rows written, and duration D1 reported for it. See D1StatementObserver.
transactions?
optional transactions?: D1TransactionMode;What transaction() does. Defaults to throw, because D1 has no transactions; unsafe-nonatomic accepts the call and gives up atomicity. See D1TransactionMode.
D1DriverOptions
Properties
onStatement?
optional onStatement?: D1StatementObserver;transactions?
optional transactions?: D1TransactionMode;D1Meta
Properties
changes
changes: number;Rows written by the statement. D1 reports 0 for reads.
duration?
optional duration?: number;Wall time D1 spent on the statement, in milliseconds.
last_row_id
last_row_id: number;Rowid of the last inserted row, meaningful only after an insert.
rows_read?
optional rows_read?: number;Rows D1 scanned. Billed, and absent on some responses.
rows_written?
optional rows_written?: number;Rows D1 persisted. Billed, and absent on some responses.
D1PreparedStatement
Methods
all()
all(): Promise<D1Result>;Returns
Promise<D1Result>
bind()
bind(...values): D1PreparedStatement;Parameters
values
...unknown[]
Returns
D1Result
Properties
meta
meta: D1Meta;results
results: Record<string, unknown>[];D1StatementReport
What one executed statement cost, as D1 reported it.
Properties
durationMs
durationMs: number;Wall time D1 spent, in milliseconds. 0 when D1 omits it.
kind
kind: string;The operation that produced it: select, insert, update, and so on.
rowsRead
rowsRead: number;Rows D1 scanned. 0 when D1 omits the figure, never estimated.
rowsWritten
rowsWritten: number;Rows D1 persisted. 0 when D1 omits the figure, never estimated.
table
table: string | undefined;The table it targeted, absent for a raw statement.
Type Aliases
D1StatementObserver
type D1StatementObserver = (report) => void;Called after each statement the driver executes.
D1 bills on rows read and written, and its analytics report per database rather than per query, so these numbers are the only way to attribute cost to the query or the request that caused it. They ride along on responses the driver already reads, so observing them costs no extra statement and no extra billable operation.
It runs on the hot path, once per statement, so keep it cheap.
Parameters
report
Returns
void
D1TransactionMode
type D1TransactionMode = "throw" | "unsafe-nonatomic";How the driver answers a transaction() call.
throwrefuses, because D1 cannot honour it. The default.unsafe-nonatomicaccepts and runs each statement immediately, each committing on its own. A failure part-way leaves the earlier writes persisted, with no rollback. For code shared with a backend that does have transactions, where the alternative is not running at all.
Functions
createD1Database()
function createD1Database(binding, options?): D1Database;Wraps a D1 binding in a Database.
import { createD1Database } from "@pitlane/data-table-d1";
import { env } from "cloudflare:workers";
let db = createD1Database(env.DB);
let posts = await db.query(Post).all();Parameters
binding
The D1 binding, e.g. env.DB.
options?
Database options, plus onStatement and transactions.