---
title: Local development · Cloudflare D1 docs
description: 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.
lastUpdated: 2025-09-30T16:36:58.000Z
chatbotDeprioritize: false
source_url:
  html: https://developers.cloudflare.com/d1/best-practices/local-development/
  md: https://developers.cloudflare.com/d1/best-practices/local-development/index.md
---

D1 has fully-featured support for local development, running the same version of D1 as Cloudflare runs globally. Local development uses [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), the command-line interface for Workers, to manage local development sessions and state.

## Start a local development session

Note

This guide assumes you are using [Wrangler v3.0](https://blog.cloudflare.com/wrangler3/) or later.

Users new to D1 and/or Cloudflare Workers should visit the [D1 tutorial](https://developers.cloudflare.com/d1/get-started/) to install `wrangler` and deploy their first database.

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](https://developers.cloudflare.com/workers/wrangler/configuration/#d1-databases) of `DB` would be available to your Worker when running locally.

To start a local development session:

1. Confirm you are using wrangler v3.0+.

   ```sh
   wrangler --version
   ```

   ```sh
   ⛅️ wrangler 3.0.0
   ```

2. Start a local development session

   ```sh
   wrangler dev
   ```

   ```sh
   ------------------
   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 --remote
   Your 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 configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) would resemble the following:

* wrangler.jsonc

  ```jsonc
  {
    "$schema": "./node_modules/wrangler/config-schema.json",
    "d1_databases": [
      {
        "binding": "DB",
        "database_name": "test-db",
        "database_id": "c020574a-5623-407b-be0c-cd192bab9545"
      }
    ]
  }
  ```

* wrangler.toml

  ```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, set `"remote" : true` in the D1 binding configuration. Refer to the [remote bindings documentation](https://developers.cloudflare.com/workers/development-testing/#remote-bindings) for more information. Any changes you make when running against a remote database cannot be undone.

Refer to the [`wrangler dev` documentation](https://developers.cloudflare.com/workers/wrangler/commands/#dev) 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](https://developers.cloudflare.com/pages/) by creating a minimal [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) 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.

Local development for remote databases

It is currently not possible to develop against a *remote* D1 database when using [Cloudflare Pages](https://developers.cloudflare.com/pages/).

Your [Wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) should resemble the following:

* wrangler.jsonc

  ```jsonc
  {
    "$schema": "./node_modules/wrangler/config-schema.json",
    "d1_databases": [
      {
        "binding": "DB",
        "database_name": "YOUR_DATABASE_NAME",
        "database_id": "the-id-of-your-D1-database-goes-here",
        "preview_database_id": "DB"
      }
    ]
  }
  ```

* wrangler.toml

  ```toml
  # If you are only using Pages + D1, you only need the below in your Wrangler config file 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:

```bash
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

Note

By default, in Wrangler v3 and above, data is persisted across each run of `wrangler dev`. If your local development and testing requires or assumes an empty database, you should start with a `DROP TABLE <tablename>` statement to delete existing tables before using `CREATE TABLE` to re-create them.

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](https://miniflare.dev/) 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](https://miniflare.dev/storage/d1) to create D1 databases you can use for testing:

* wrangler.jsonc

  ```jsonc
  {
    "$schema": "./node_modules/wrangler/config-schema.json",
    "d1_databases": [
      {
        "binding": "DB",
        "database_name": "test-db",
        "database_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
      }
    ]
  }
  ```

* wrangler.toml

  ```toml
  [[d1_databases]]
  binding = "DB"
  database_name = "test-db"
  database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  ```

```js
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:

```js
const db = await mf.getD1Database("DB");


const stmt = db.prepare("SELECT name, age FROM users LIMIT 3");
const { results } = await stmt.run();


console.log(results);
```

### `unstable_dev`

Wrangler exposes an [`unstable_dev()`](https://developers.cloudflare.com/workers/wrangler/api/) that allows you to run a local HTTP server for testing Workers and D1. Run [migrations](https://developers.cloudflare.com/d1/reference/migrations/) against a local database by setting a `preview_database_id` in your Wrangler configuration.

Given the below Wrangler configuration:

* wrangler.jsonc

  ```jsonc
  {
    "$schema": "./node_modules/wrangler/config-schema.json",
    "d1_databases": [
      {
        "binding": "DB",
        "database_name": "your-database",
        "database_id": "<UUID>",
        "preview_database_id": "local-test-db"
      }
    ]
  }
  ```

* wrangler.toml

  ```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`:

```sh
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.

```ts
import { 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()`](https://developers.cloudflare.com/workers/wrangler/api/#usage) documentation for more details on how to use the API within your tests.

## Related resources

* Use [`wrangler dev`](https://developers.cloudflare.com/workers/wrangler/commands/#dev) to run your Worker and D1 locally and debug issues before deploying.
* Learn [how to debug D1](https://developers.cloudflare.com/d1/observability/debug-d1/).
* Understand how to [access logs](https://developers.cloudflare.com/workers/observability/logs/) generated from your Worker and D1.
