# Database

Every space has its own SQLite database, shared by all its pages, so a page keeps data between visits.

**When to use it:** The page keeps data between visits: lists, scores, answers, a tracker. Members add or edit it; everyone can read it.

**Status:** Live. **Turned on:** Always on. Visitors can read it while "Pages can show saved data" is on in the space's Settings (the default for public spaces).

## How

Schema and seed data go in db/NNN_name.sql next to the page and run on publish. In the page, cloudtifact.db.query(sql, params) reads and cloudtifact.db.exec(sql, params) writes.

```sql
-- db/001_schema.sql, next to the page. Runs once, on publish.
CREATE TABLE IF NOT EXISTS ideas (id INTEGER PRIMARY KEY, text TEXT NOT NULL, votes INTEGER DEFAULT 0);
```

```html
<ul id="ideas"></ul>
<script>
  (async () => {
    const { rows } = await cloudtifact.db.query(
      "SELECT text, votes FROM ideas ORDER BY votes DESC", [], { objects: true });
    document.getElementById("ideas").innerHTML =
      rows.map((r) => `<li>${r.text} (${r.votes})</li>`).join("");
  })();
  // Members viewing the page in the Cloudtifact app can write:
  // await cloudtifact.db.exec("INSERT INTO ideas (text) VALUES (?)", [text]);
</script>
```

## Limits

- Writes from the page need a signed-in member viewing it in the Cloudtifact app; a direct link is read-only. For visitors, use guest writes.
- Use ? placeholders and pass values in params; never build SQL from what people type.
- Visitors read the whole database while the space lets pages show saved data (on by default for public spaces), so don't keep secrets in it.

## Errors

A failed call rejects with an Error whose `code` is one of:

| Code | When |
| --- | --- |
| `read_only` | exec() without write access: a direct link, or a visitor. Handle it; don't retry. |
| `forbidden` | Visitors can't read this space's database (the space turned that off). |
| `bad_request` | SQLite refused the statement; the message has its reason. |
| `invalid` | The arguments are wrong (sql must be a string, params an array). |
| `network` | The request didn't reach Cloudtifact (offline, blocked). |
| `timeout` | No answer in time; try again. |

## Cost

Included in every plan.
