> Fetch the index of every Tonbo docs page as Markdown at https://tonbo.dev/llms.txt

# gate() and import

Last updated 29 October 2026

`gate()` holds the outcome of a unit of work until `synced()`, and `importSqlite()` loads a plain SQLite file into a new database.

## gate()

From `@tonbo/sqlite-ursula` and `/tonbo`.

```text
function gate<T>(
  db: UrsulaDatabase | readonly UrsulaDatabase[],
  work: () => T | Promise<T>,
  options?: GateOptions,
): Promise<Awaited<T>>

interface GateOptions {
  signal?: AbortSignal;
}
```

Runs `work` and settles with its outcome only once a [synced()](https://tonbo.dev/docs/reference/nodejs/database.md#synced) call made after `work` settled resolved, on every database in `db`, in parallel. It applies the rule to whatever acts on the outcome: a handler’s `Response`, a job’s promise a queue framework acknowledges, a message you acknowledge yourself.

```js
import { gate, open } from "@tonbo/sqlite-ursula/tonbo";

const db = await open("notes");

// A fetch-style handler: the status, the headers and every body chunk wait.
const handler = (request) => gate(db, () => router.fetch(request), { signal: request.signal });

// A queue you acknowledge yourself.
for await (const message of subscription) {
  await gate(db, () => applyMessage(db, message)); // records message.id in the same transaction
  await message.ack(); // a crash before this redelivers, and applyMessage skips a seen id
}
```

- Reads wait too: a result may have read another call’s pending commit, or state this process is about to lose to a takeover.
- `work`’s errors wait as its results do, except an `UrsulaError`, which is thrown at once, also when a library wrapped it (as a `cause`, or in an `AggregateError`). A reset during the wait rejects with the `UrsulaResetError` and drops the outcome.
- When `work` returns a `Response` with a body, the gate resolves with one of the same status and headers whose body passes each chunk, and its end, on only once a `synced()` made after the chunk was read resolved. One call covers every chunk read while the previous one was in flight, and the gate reads ahead at most 64 KiB plus one chunk. A reset errors the body.
- `signal` bounds the wait for the outcome, which for a `Response` is its status and headers: the gate then rejects with `signal.reason`, cancels a returned body, and the commits keep replicating. It does not cut short a body that has started: cancel the body to stop it.
- An empty array throws `TypeError`. A `node:http` response is not gated by itself: write it after the gate, as in `res.end(await gate(db, () => save(req)))`.

The gate does not cover effects inside `work` before it returns, such as an outbound call or an acknowledgement in the middle of a handler (await `db.synced()` before them); WebSocket frames, raw sockets, timers, background jobs and child processes; or exactly-once effects. A crash after the release but before an acknowledgement runs the work again, so record message ids and idempotency keys in the same transaction as the work.

### GateOptions

| Option | Default | What it does |
| --- | --- | --- |
| `signal` | None | Stops waiting for the outcome, as `synced()`’s signal does. A signal that already fired rejects before `work` runs. |

## importSqlite()

From `@tonbo/sqlite-ursula` only.

```text
function importSqlite(url: string | URL, file: string, options?: ImportOptions): Imported

interface ImportOptions {
  token?: string;
  timeoutMs?: number;
}

interface Imported {
  offset: number;
  digest: string;
  pages: number;
  bytes: number;
}
```

Imports the plain SQLite database `file` into the stream at `url`, as the stream’s first snapshot, page for page. Then open the database as any other. On Tonbo, create the database first (see [Manage databases](https://tonbo.dev/docs/databases/manage.md)): Tonbo creates its stream empty, and the import fills it.

```js
import { importSqlite } from "@tonbo/sqlite-ursula";
import { resolveDatabase } from "@tonbo/sqlite-ursula/tonbo";

// From the resolve on, the key's Streams tokens serve the process's requests, the import's too.
const { url } = await resolveDatabase("notes");
importSqlite(url, "/tmp/notes-import.db");
```

The file must have 4096-byte pages, with no `-wal` beside it and no hot journal. It is only read. `VACUUM INTO`, run on the source database with any SQLite, writes such a copy:

```text
PRAGMA page_size = 4096;
VACUUM INTO '/tmp/notes-import.db';
```

- The stream must be missing or empty. A stream of another content type fails with `stream_format`, anything else with `stream_state`, and nothing is published.
- While the import runs, opens of the stream fail with `stream_state`. An import that fails after it claimed the stream leaves one that refuses opens and imports until it is deleted or recreated, so an `UrsulaUnavailableError` from an import says `fix`.
- A snapshot above the request limit goes up in parts. A server without a cold store fails a file whose compressed snapshot is above that limit with `invalid_argument`, naming it (see [Limits](https://tonbo.dev/docs/databases/limits.md)).
- It blocks the calling thread until the import is done, for at most `timeoutMs`.

### ImportOptions

| Option | Default | What it does |
| --- | --- | --- |
| `token` | None | The bearer token for the import alone. A string only: the import blocks its thread, so no provider could refresh it. With a provider, pass `(await provider({ signal })).token`. Without one, the process’s token serves the import: after a [resolveDatabase()](https://tonbo.dev/docs/reference/nodejs/resolve-database.md), the API key’s tokens, which the extension keeps fresh while the import runs (since 0.11.2). |
| `timeoutMs` | 10 minutes | How long the import may take: a positive whole number of milliseconds. |

### Imported

| Field | What it holds |
| --- | --- |
| `offset` | The snapshot's stream offset, the end of the import's claim. |
| `digest` | The snapshot's digest. |
| `pages` | How many pages it holds. |
| `bytes` | The compressed snapshot's size. |

It throws [UrsulaSetupError](https://tonbo.dev/docs/reference/nodejs/errors.md#ursula-setup-error) (such as `invalid_argument` for the file, `stream_format`, `stream_state`, `protocol_mismatch` or `no_server_identity`) and `UrsulaUnavailableError`. To export a database to a plain SQLite file, see [Export a copy](https://tonbo.dev/docs/databases/backups.md#export).
