Develop locally
D1 has fully-featured support for local development, running the same version of D1 as Cloudflare runs globally. Local development uses Wrangler, the command-line interface for Workers, to manage local development sessions and state.
Start a local development session
Local development sessions create a standalone, local-only environment that mirrors the production environment D1 runs in so that you can test your Worker and D1 before you deploy to production.
An existing D1 binding of DB
would be available to your Worker when running locally.
To start a local development session:
# Confirm we are using wrangler v3.0+
$ wrangler --version
⛅️ wrangler 3.0.0
# Start a local dev session:
$ wrangler dev
# Outputs:
------------------wrangler dev now uses local mode by default, powered by 🔥 Miniflare and 👷 workerd.To run an edge preview session for your Worker, use wrangler dev --remoteYour worker has access to the following bindings:- D1 Databases: - DB: test-db (c020574a-5623-407b-be0c-cd192bab9545)⎔ Starting local server...
[mf:inf] Ready on http://127.0.0.1:8787/[b] open a browser, [d] open Devtools, [l] turn off local mode, [c] clear console, [x] to exit │
In this example, the Worker has access to local-only D1 database. The corresponding D1 binding in your wrangler.toml
configuration file would resemble the following:
wrangler.toml[[d1_databases]]
binding = "DB"
database_name = "test-db"
database_id = "c020574a-5623-407b-be0c-cd192bab9545"
Note that wrangler dev
separates local and production (remote) data. A local session does not have access to your production data by default. To access your production (remote) database, pass the --remote
flag when calling wrangler dev
. Any changes you make when running in --remote
mode cannot be undone.
Refer to the wrangler dev
documentation to learn more about how to configure a local development session.
Develop locally with Pages
You can only develop against a local D1 database when using Cloudflare Pages by creating a minimal wrangler.toml
in the root of your Pages project. This can be useful when creating schemas, seeding data or otherwise managing a D1 database directly, without adding to your application logic.
Your wrangler.toml
should resemble the following:
wrangler.toml# If you are only using Pages + D1, you only need the below in your wrangler.toml to interact with D1 locally.
[[d1_databases]]
binding = "DB" # Should match preview_database_id
database_name = "YOUR_DATABASE_NAME"
database_id = "the-id-of-your-D1-database-goes-here" # wrangler d1 info YOUR_DATABASE_NAME
preview_database_id = "DB" # Required for Pages local development
You can then execute queries and/or run migrations against a local database as part of your local development process by passing the --local
flag to wrangler:
$ wrangler d1 execute YOUR_DATABASE_NAME \ --local --command "CREATE TABLE IF NOT EXISTS users ( user_id INTEGER PRIMARY KEY, email_address TEXT, created_at INTEGER, deleted INTEGER, settings TEXT);"
The preceding command would execute queries the local only version of your D1 database. Without the --local
flag, the commands are executed against the remote version of your D1 database running on Cloudflare’s network.
Persist data
Use wrangler dev --persist-to=/path/to/file
to persist data to a specific location. This can be useful when working in a team (allowing you to share) the same copy, when deploying via CI/CD (to ensure the same starting state), or as a way to keep data when migrating across machines.
Users of wrangler 2.x
must use the --persist
flag: previous versions of wrangler did not persist data by default.
Test programmatically
Miniflare
Miniflare allows you to simulate a Workers and resources like D1 using the same underlying runtime and code as used in production.
You can use Miniflare’s support for D1 to create D1 databases you can use for testing:
wrangler.toml[[d1_databases]]
binding = "DB"
database_name = "test-db"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
const mf = new Miniflare({ d1Databases: { DB: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" },
});
You can then use the getD1Database()
method to retrieve the simulated database and run queries against it as if it were your real production D1 database:
const db = await mf.getD1Database("DB");
const stmt = db.prepare("SELECT name, age FROM users LIMIT 3");
const { results } = await stmt.all();
console.log(results);
unstable_dev
Wrangler exposes an unstable_dev()
that allows you to run a local HTTP server for testing Workers and D1. Run migrations against a local database by setting a preview_database_id
in your wrangler.toml
configuration.
Given the below wrangler.toml
configuration:
wrangler.toml[[ d1_databases ]]
binding = "DB" # i.e. if you set this to "DB", it will be available in your Worker at `env.DB`
database_name = "your-database" # the name of your D1 database, set when created
database_id = "<UUID>" # The unique ID of your D1 database, returned when you create your database or run `
preview_database_id = "local-test-db" # A user-defined ID for your local test database.
Migrations can be run locally as part of your CI/CD setup by passing the --local
flag to wrangler
:
$ wrangler d1 migrations apply your-database --local
Usage example
The following example shows how to use Wrangler’s unstable_dev()
API to:
- Run migrations against your local test database, as defined by
preview_database_id
. - Make a request to an endpoint defined in your Worker. This example uses
/api/users/?limit=2
. - Validate the returned results match, including the
Response.status
and the JSON our API returns.
index.test.tsimport { unstable_dev } from "wrangler";
import type { UnstableDevWorker } from "wrangler";
describe("Test D1 Worker endpoint", () => { let worker: UnstableDevWorker;
beforeAll(async () => { // Optional: Run any migrations to set up your `--local` database // By default, this will default to the preview_database_id execSync( `NO_D1_WARNING=true wrangler d1 migrations apply db --local` );
worker = await unstable_dev("src/index.ts", { experimental: { disableExperimentalWarning: true }, }); });
afterAll(async () => { await worker.stop(); });
it("should return an array of users", async () => { // Our expected results const expectedResults = `{"results": [{"user_id": 1234, "email": "foo@example.com"},{"user_id": 6789, "email": "bar@example.com"}]}` // Pass an optional URL to fetch to trigger any routing within your Worker const resp = await worker.fetch("/api/users/?limit=2"); if (resp) { // https://jestjs.io/docs/expect#tobevalue expect(resp.status).toBe(200) const data = await resp.json(); // https://jestjs.io/docs/expect#tomatchobjectobject expect(data).toMatchObject(expectedResults) } });
});
Review the unstable_dev()
documentation for more details on how to use the API within your tests.
Related resources
- Use
wrangler dev
to run your Worker and D1 locally and debug issues before deploying. - Learn how to debug D1.
- Understand how to access logs generated from your Worker and D1.