req.env.DB in every handler the moment you add the binding to your Env type — no connection pooling, no client initialization, just prepare → bind → execute.
Configure the binding
1
Add the database to wrangler.toml
wrangler.toml
2
Define the Env type
src/types/env.ts
3
Pass Env to createApp
src/index.ts
Querying rows
D1 uses a prepared-statement API. Call.prepare(sql) to create a statement, .bind(...values) to safely interpolate parameters, then either .first() for a single row or .all() for a full result set.
The example below implements a paginated user listing using page and limit query parameters:
Inserting and updating
Use.run() for statements that modify data. When you append RETURNING id to an INSERT, switch to .first() to retrieve the generated ID in a single round-trip.
Batch statements
req.env.DB.batch([...]) executes multiple prepared statements in a single round-trip and treats them as an atomic unit — if one statement fails, none of the changes are committed. Use batching whenever you need to insert or update multiple related rows together.
Handling no results
.first() returns null when no row matches. Always check for null before accessing row properties and return a 404 so your client receives a meaningful response.
TypeScript row types
Pass your row shape as a generic argument to.first<T>() or .all<T>(). D1 returns plain objects, so the generic is a cast — align it with your actual schema to avoid surprises.
D1 is eventually consistent for read replicas in multi-region deployments.
Writes go to the primary region first and propagate to read replicas within
seconds. If your application needs immediate read-your-write consistency after
an insert or update, direct that follow-up read to the primary by using
wrangler d1 migrations and testing locally with --local to verify
behavior before deploying.