D1-compatible SQLite
A SQLite database bound to env.NAME, with the D1 prepared-statement API and online backups.
A SQLite database with the D1 binding API. Each database is its own SQLite file.
Configuration
"d1_databases": ["DB"]
Bare names are auto-provisioned on deploy; see Storage resources.
Usage
interface User {
id: number;
name: string;
}
export default {
async fetch(request, env, ctx) {
const id = Number(new URL(request.url).searchParams.get("id"));
const user = await env.DB.prepare("SELECT * FROM users WHERE id = ?").bind(id).first<User>();
const name = await env.DB.prepare("SELECT name FROM users WHERE id = ?").bind(id).first<string>("name");
const inserted = await env.DB.prepare("INSERT INTO users (name) VALUES (?)").bind("Ada").run();
// inserted.meta.last_row_id, inserted.meta.changes
const { results } = await env.DB.prepare("SELECT * FROM users").all<User>();
await env.DB.batch([
env.DB.prepare("UPDATE users SET name = ? WHERE id = ?").bind("Grace", id),
env.DB.prepare("DELETE FROM users WHERE id = ?").bind(id + 1),
]);
return Response.json({ user, name, id: inserted.meta.last_row_id, results });
},
} satisfies ExportedHandler<Env>;
exec() runs a SQL script with no parameters, which is what multi-statement DDL needs. Keep the schema in migrations, and do not run CREATE TABLE on every request:
sproutboat d1 migrations create DB init
Promises and the compatibility date
From compatibility date 2026-10-24, prepare().raw(), .run(), .all(), .first(), batch() and exec() return Promises, as on Workers. Before that date they are synchronous and return their value directly. await works at both, so the code above is correct at every date.
A call that fails rejects its Promise (a foreign-key violation, say, which is enforced from 2026-10-24). If you do not await it, nothing catches the error, and a try / catch around the call does not either. As on Workers the rejection is reported (a log line, and an unhandledrejection event when you listen for one) and the process and your other requests carry on; the write still failed. Await the call to handle the error.
Methods
| Method | Signature | Returns |
|---|---|---|
prepare(sql) |
SQL with ? parameters |
a prepared statement |
.bind(...values) |
strings, numbers, booleans, null or bytes |
the statement |
.first(column?) |
(a Promise from 2026-10-24) the first row, or one column of it; null when there are no rows |
|
.run() |
(a Promise from 2026-10-24) { results, success, meta: { last_row_id, changes, ... } } |
|
.all() |
(a Promise from 2026-10-24) { results, success, meta } |
|
.raw() |
(a Promise from 2026-10-24) rows as arrays of column values |
|
batch(statements) |
prepared statements | (a Promise from 2026-10-24) one result per statement |
exec(sql) |
SQL script, no parameters | (a Promise from 2026-10-24) runs every statement; use it for multi-statement DDL |
backup(name?) |
{ path, bytes } |
backup() is a Sproutboat addition to the D1 API, so a handler that calls it does not run on Workers. It writes an online, integrity-checked copy of the database with VACUUM INTO, named name or a timestamp. On a standalone binary it lands in <data>/backups/. On the platform it is written on the server next to the database.