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

# Rules: persist, then act

Last updated 29 October 2026

Check these rules before your program replies, acknowledges or calls another service after a commit: each keeps a commit from being announced before Tonbo holds it.

The examples use the connection `open()` returns, a node:sqlite `DatabaseSync`, as [Connect](https://tonbo.dev/docs/databases/connect.md) opens it.

### Await synced() before anything outside the process acts

A commit returns once it is on this machine, and losing the machine loses it until `synced()` resolves (see [When a commit is durable](https://tonbo.dev/docs/databases/how-it-works.md#commit)). A reply, an acknowledgement or a payment that goes out first can outlive the commit it reports.

**Do**

```js
db.prepare("INSERT INTO orders(id, item) VALUES (?, ?)").run(order.id, order.item);
await db.synced(); // Tonbo holds the order
reply("ordered");
```

**Don’t**

```js
db.prepare("INSERT INTO orders(id, item) VALUES (?, ?)").run(order.id, order.item);
reply("ordered"); // the order is only on this machine
```

### Let gate() hold a handler’s answer

[gate()](https://tonbo.dev/docs/reference/nodejs/gate-and-import.md#gate) settles with the work’s outcome only once a `synced()` made after the work resolved, so no path of the handler can forget it. Reads wait too: a result may have read another call’s commit that Tonbo does not hold yet.

**Do**

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

const db = await open("notes");
const handler = (request) => gate(db, () => router.fetch(request), { signal: request.signal });
```

**Don’t**

```js
const handler = async (request) => {
  const response = await router.fetch(request);
  if (request.method !== "GET") await db.synced(); // a GET may have read an unsynced commit
  return response;
};
```

### Record a side effect before you make it

Record the intent with an idempotency key derived from the task, await `synced()`, then make the call. A call made before its record repeats after a crash, and a random key looks new to the provider every time. The whole pattern, with recovery, is [Run side effects once](https://tonbo.dev/docs/databases/side-effects.md).

**Do**

```js
// The same key on every run of this task; perform() records, syncs, then calls.
await perform(`task-${task.id}/welcome-email`, { to: task.email, template: "welcome" }, () =>
  db.prepare("UPDATE tasks SET step = 'welcomed' WHERE id = ?").run(task.id),
);
```

**Don’t**

```js
await fetch("https://api.example.com/v1/emails", {
  method: "POST",
  headers: { "Idempotency-Key": crypto.randomUUID() }, // a new key on every run
  body: JSON.stringify({ to: task.email, template: "welcome" }),
});
db.prepare("UPDATE tasks SET step = 'welcomed' WHERE id = ?").run(task.id); // after the call
```

### Bound a wait where someone is waiting

`synced()` waits through an outage instead of throwing, and the commits keep replicating. Where a caller waits for the answer, pass a `signal` so it rejects in time (see [synced()](https://tonbo.dev/docs/reference/nodejs/database.md#synced)).

**Do**

```js
await db.synced({ signal: AbortSignal.timeout(10_000) }); // rejects after 10 seconds
```

**Don’t**

```js
await db.synced(); // in a request handler: waits as long as Tonbo is unreachable
```
