D1’s Developer Workflow
From the start, the D1 team leaned into Wrangler as the primary interface for building and managing databases. With Wrangler 2.0, that workflow now covers everything from initial setup to local testing and production backups.
Creating and bootstrapping a database
Creating a fresh D1 instance is a single command:
npx wrangler d1 create my_database_name
Importing existing SQLite data is just as direct — point Wrangler at a .sql file and the schema and contents are loaded into your new database:
wrangler d1 execute my_database-name --file ./filename.sql
Local development and testing
D1 integrates with Wrangler’s local mode. Running wrangler dev -–local -–persist creates a local SQLite file in .wrangler/state that you can inspect with familiar tools. GUI clients like SQLiteFlow or Beekeeper work fine, and the SQLite command line is always an option:
sqlite3 .wrangler/state/d1/DB.sqlite3
Backups and restoration
Wrangler handles snapshots as a first-class operation, and for the beta the service itself takes hourly automatic backups of your data, stored in R2, so rollback is always possible. Backups are plain SQLite files — you can download one and drop it into the .wrangler/state directory to reproduce a production bug locally. Running wrangler dev –-local –-persist against that directory picks the backup up automatically.
Dashboard parity
Not every developer lives in a terminal. D1 is also available from the Cloudflare dashboard, covering the same essentials as Wrangler: bootstrapping, creating and updating tables, browsing data, and triggering backups. Changes made in the UI are immediately available to your Worker — no additional deploy step.
Transactions: Why SQL Alone Isn’t Enough
D1’s simplified interface deliberately masks a lot of operational complexity. A major beneficiary of that abstraction is the transaction model. While .batch() lets you send multiple SQL statements to be executed as a transaction under the hood, it doesn’t cover cases where you want to interleave JavaScript logic with SQL statements in a single atomic unit.
Running BEGIN TRANSACTION directly in D1 returns an error, and the reasoning is rooted in how the system physically works. SQL executes inside your D1 database — writes at the single primary, reads at the nearest replica. Your Worker code, however, runs near the client, often half a world away. SQLite allows only one open write transaction. If D1 allowed arbitrary BEGIN TRANSACTION calls, any Worker request anywhere could lock the entire database, either because a crashed Worker never issues ROLLBACK, or simply because long-lived multi-round-trip transactions could block the primary for seconds at a time.
A Better Idea Than Stored Procedures
Database-level stored procedures seem like the classic solution — execute code adjacent to the data. But in practice they’re notoriously awkward: they typically require a different language, a separate development lifecycle, and a manual, fragile deployment process. D1’s answer is to apply the same idea using the platform it’s built on: Worker code itself.
The proposal, currently being tested with private beta users, binds a "Procedures" file to a D1 database through wrangler.toml. Within that file, a new db.transaction() API becomes available. The procedures are nothing more than exported functions that combine JavaScript and SQL. Back in your main Worker, the same methods are exposed through the database binding’s Procedures property.
If multiple procedures fire at once, only one db.transaction() function is active per instance. Other write transactions queue, while reads continue to run against local replicas without interruption.
Feedback Sought on Procedure Design
This design is a working proposal, not the finished API, and Cloudflare is inviting feedback from beta users. The intended benefits are clear:
- Transaction code runs as close to the database as possible, removing network round trips while the transaction is open.
- An exception or cancellation triggers instant rollback — a stale open transaction that blocks the instance is impossible.
- Procedures are part of the same TypeScript/JavaScript build as the rest of your Worker code, not a separate project or dialect.
- Deployment is seamless, and versions are bound: rolling back a Worker also rolls back its procedures, so code skew between Worker and database is eliminated.
- Local development and test execute the procedure without the network hop, as if it were a plain local function.
Community-built tools around D1
Feedback and feature requests are a big part of any private beta, but with D1 we've also been impressed by the number of tools users have built on top of the product. Some of these come from Cloudflare developers, others from community members. A few of the most useful additions have been shared publicly and are worth a closer look.
workers-qb
Writing raw SQL is powerful, and using D1's .bind() API keeps it safe from SQL injection, but raw syntax can get unwieldy. Most existing query builders assume direct access to the underlying database, which makes them a poor fit for D1. To fill that gap, Cloudflare developer Gabriel Massadas built workers-qb, a small, zero-dependency query builder designed specifically for D1:
import { D1QB } from 'workers-qb'
const qb = new D1QB(env.DB)
const fetched = await qb.fetchOne({
tableName: "employees",
fields: "count(*) as count",
where: {
conditions: "active = ?1",
params: [true]
},
})
Full documentation is available on the project homepage.
D1 console
Wrangler and the Cloudflare dashboard both let you interact with D1, but if you want to run a quick series of queries, those aren't always the most efficient path. Community champion Isaac McFadyen created the first D1 console, which lets you execute queries directly from your terminal. Instead of typing out a sequence of Wrangler commands, you can just run your SQL.
The console includes the standard features you'd expect from a modern database client: multiline input, command history, validation for things D1 may not yet support, and the ability to save your Cloudflare credentials between sessions. It's available on GitHub and NPM.
Miniflare test integration
Miniflare, the project that powers Wrangler's local development experience, also provides full test environments for Jest and Vitest. One of the key features there is Isolated Storage, which keeps each test run in its own isolated state so changes in one test don't leak into another. Creator Brendan Coll extended that same capability to D1:
import Worker from ‘../src/index.ts’
const { DB } = getMiniflareBindings();
beforeAll(async () => {
// Your D1 starts completely empty, so first you must create tables
// or restore from a schema.sql file.
await DB.exec(`CREATE TABLE entries (id INTEGER PRIMARY KEY, value TEXT)`);
});
// Each describe block & each test gets its own view of the data.
describe(‘with an empty DB’, () => {
it(‘should report 0 entries’, async () => {
await Worker.fetch(...)
})
it(‘should allow new entries’, async () => {
await Worker.fetch(...)
})
])
// Use beforeAll & beforeEach inside describe blocks to set up particular DB states for a set of tests
describe(‘with two entries in the DB’, () => {
beforeEach(async () => {
await DB.prepare(`INSERT INTO entries (value) VALUES (?), (?)`)
.bind(‘aaa’, ‘bbb’)
.run()
})
// Now, all tests will run with a DB with those two values
it(‘should report 2 entries’, async () => {
await Worker.fetch(...)
})
it(‘should not allow duplicate entries’, async () => {
await Worker.fetch(...)
})
])
The test databases run entirely in-memory, making them fast enough that slow tests aren't a distraction. Reliable, quick testing is a big part of building maintainable applications, and we're glad D1 integrations now get that same benefit.
Joining the private beta
We're learning a lot from the ways beta users experiment with D1 at this stage, and we're looking for more of that input as we move toward an open beta. Access to the private beta is being granted gradually, but if you haven't received an invitation yet you can sign up here. Invited users will receive an official welcome email to get started.



