---
url: https://docs.forestfuture.dev/d1-record/guide/console.md
description: >-
  How to open a console on your D1 database with your Models loaded: d1-record
  console, what is in scope, its options, opening the remote database with
  --remote, and where its data and history live.
---

# Console

Open a console on your local database, with your Models loaded, to try a query or fix a record by hand, or on your remote one with [`--remote`](#opening-your-remote-database). Run it from the folder with your Wrangler config:

```sh
npx d1-record console
```

It prints which database and Models it opened, then waits for a line:

```text
Local database DB (.wrangler/state)
Models: Post, User

local> const user = await User.find("c5c765b4-b7e1-428e-aa00-88d58f622c71")
#=> undefined
local> user
#=> User { id: 'c5c765b4-b7e1-428e-aa00-88d58f622c71', name: 'Alice', email: [FILTERED] }
local> await user.update({ name: "Johnny" })
#=> true
local> const admins = await User.where({ role: "admin" }).toArray()
#=> undefined
```

Leave with `quit`, `exit`, or Ctrl-D. Ctrl-C stops a line that’s still running. `.help` lists the console’s own commands, and `npx d1-record c` is the short form.

## What you can use

* **Your Models,** by class name, from every file in `src/models`. They query your database, local unless you pass `--remote`, with no `db` to pass.
* **`db`,** your connected database, for `db.batch([...])` and `db.model(...)`.
* **Top-level `await`,** and `const` and `let`, which last until you leave.

A record prints as it does in a log: its class and attributes, with a filtered attribute as `[FILTERED]` and an unsaved change next to the value it had. [Printing records](./models#printing-records) has the details.

An error prints and the console carries on.

## Reloading your Models

After you edit a Model, or a file it imports, read them again without leaving:

```text
> reload!
Reloaded: Post, User
```

`.reload` does the same. A Model you added appears, and one whose file you removed goes. Records and values you already hold keep their old class, so run the lines that made them again.

If a file fails to load, say a syntax error in the middle of an edit, the console names the file and keeps the Models you had.

::: warning TIP
Only files in your project’s folder are read again. Your database connection and `@forestfuture/d1-record` stay as they are.
:::

## Seeing the SQL

Start with `--verbose` to see each statement a line runs, or turn it on and off while you work with `.verbose`:

```text
> .verbose
SQL logging on
> await User.find(1)
  User  1.2ms  SELECT * FROM "users" WHERE (("id" = ?)) LIMIT ?  [ 1, 1 ]
#=> User { id: 1, name: 'Alice', email: [FILTERED] }
```

Each line shows the Model, the time, the SQL, and the values it bound. Filtered attributes show as `[FILTERED]`, as they do in `onQuery`. A statement that fails prints `failed` and the reason in place of the time. `verbose!` toggles it too, and `.verbose on` and `.verbose off` set it explicitly.

::: warning TIP
The SQL comes from the database’s own `onQuery`, so a listener you set on your base class still runs as well.
:::

## Choosing what to open

| Option              |                                                                                                         |
| ------------------- | ------------------------------------------------------------------------------------------------------- |
| `--database <name>` | The D1 binding to open, when your Wrangler config has several.                                          |
| `--models <path>`   | Where your Models are: a folder (`src/models` by default) or one file (`src/models.ts`).                |
| `--config <file>`   | Use this Wrangler config instead of finding `wrangler.jsonc`, `wrangler.json`, or `.toml`.              |
| `--no-history`      | Don’t keep what you type in the history file.                                                           |
| `--verbose`         | Print each statement a line runs, to begin with. `.verbose` turns it on and off.                        |
| `-e, --eval <code>` | Run this code, print its result, and exit. See [Running one piece of code](#running-one-piece-of-code). |
| `--remote`          | Open your remote database instead, after you type its name. See below.                                  |
| `--yes`             | With `--remote`, don’t ask first. It’s also how to open it without a terminal.                          |
| `--history`         | Keep what you type in a remote session, which isn’t kept unless you ask.                                |

## Where your data lives

The console opens the local database in `.wrangler/state`, beside your Wrangler config. It’s the same one `wrangler dev` and `wrangler d1 migrations apply --local` use, so apply your migrations first, and what you change in the console is what your dev server sees.

It opens your remote database only when you ask with `--remote`, and never for a binding your config marks `remote: true` by itself.

It starts only that D1 binding. The rest of your Wrangler config (Durable Objects, services, `vars`, and the secrets in `.dev.vars`) isn’t started, so it can’t warn or fail on them, and your Models can’t reach them either.

On a terminal, what you type is kept in `.wrangler/d1-record-history`, which Wrangler projects already ignore. A line can hold a token or an email, so add `--no-history` when you’d rather it wasn’t kept.

## Opening your remote database

`--remote` opens the real database your binding points at, to look at production data or fix one record by hand:

```sh
npx d1-record console --remote
```

It names the database and waits for you to type its name. Anything else, including `y`, cancels and opens nothing. Anything you type or paste after the name is dropped, so a pasted block can’t run on the real database before you’ve seen the prompt:

```text
Remote database DB (shop, id 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d)
What you change in the console is changed in this database, not a copy.
Type its name to continue, anything else cancels: shop
```

Once it’s open, the prompt says `remote>` (in red on a terminal) where a local session says `local>`, so the two can’t be mistaken for each other:

```text
Remote database DB (shop, id 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d)
Models: Post, User

remote> await User.count()
#=> 42
```

::: warning Changes can’t be undone
A change is made to the real database, for good. `await user.destroy()` deletes that row from your remote database.
:::

The console signs in with your Wrangler login, or with `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` in the environment, and takes the account from `account_id` in your Wrangler config when it names one. When Cloudflare refuses, the console prints its reason and how to sign in.

There’s no one to type the name unless both your input and output are a terminal, so a piped session refuses unless you add `--yes`, which is how a script opens it:

```sh
echo 'await User.count()' | npx d1-record console --remote --yes
```

What you type isn’t kept unless you add `--history`, and then it goes in `.wrangler/d1-record-history-remote`, a file of its own, so the up arrow never offers a local session a line you typed against the real database.

::: warning TIP
The binding needs a `database_id`. If it also has a `preview_database_id`, Wrangler opens that one in place of `database_id`, and the console says so: `Remote preview database DB (…)`.
:::

::: warning TIP
A token needs **D1: Edit** and **Workers Scripts: Edit**, and the account needs a `workers.dev` subdomain, as `wrangler dev` needs for a remote binding.
:::

## Running one piece of code

`-e` runs one piece of code with your Models and `db` in scope, prints its result, and exits, which is how to use the console from a script:

```sh
npx d1-record console -e 'await User.count()'
```

```text
42
```

* **The result is on stdout.** A string prints as it is, without quotes, so `$(…)` captures it, and anything else prints as the console prints it, without `#=> `. `undefined` prints nothing. What your code logs with `console.log` comes first, and `console.error` goes to stderr, as in `node -e`.
* **Everything else is on stderr:** which database and Models it opened, the SQL with `--verbose`, and a refusal to start.
* **A failure exits 1.** Its stack goes to stderr, no result goes to stdout, and no statement after it runs. Success exits 0.
* **Join statements with `;`.** The last value is the result: `-e 'await User.create({ name: "Dan" }); await User.count()'`.
* **It keeps no history,** ignores what is piped to it, and has no time limit: stop it with Ctrl-C, or run it under `timeout`.

::: warning TIP
Await every call. `User.find(1)` without `await` prints `Promise { <pending> }`, and a promise you don’t await may not finish before the console exits, and its failure isn’t reported.
:::

::: warning TIP
Put the code in single quotes, so your shell leaves `$`, `!`, and backticks alone. With `--remote`, `$(…)` makes stdout a pipe, so there is no terminal to ask: add `--yes`, as in `npx d1-record console --remote --yes -e 'await User.count()'`.
:::

## Running a few lines at once

Lines you paste or pipe run in order, each after the one before, and the console finishes them before it exits:

```sh
echo 'await User.count()' | npx d1-record console
```

That prints the same transcript a session does (the database, a prompt before each line, `#=> ` before each result) and exits 0 even when a line fails. For a script that checks the result or the exit code, use `-e`.

::: warning TIP
The console reads your TypeScript Models directly, including extensionless imports and `tsconfig.json` paths, so it works in TypeScript projects. It needs Node.js 22.13 or later, and `wrangler` and `@forestfuture/d1-record` installed in your project. It uses your copies, so your Models and the console share one library.
:::

::: warning Only in projects you trust
The console runs your project’s code: your Models, and the `wrangler` and `@forestfuture/d1-record` in its `node_modules`. Run it in a project you’d run `wrangler dev` in.
:::

::: rails Compared with Rails
This is `rails console` for D1: your Models in scope, and records printed as `inspect` prints them. `-e` is `rails runner` with the last value printed, which `rails runner` leaves to you.
:::

::: rails Beyond Rails
`rails console` opens whichever environment you start it in, without asking. `--remote` asks you to type the database’s name first, and its prompt says `remote>`.
:::
