SQLite
On-device SQL storage for offline-first data and local caching.
The rule
This is the whole text, exactly as your agent receives it. Nothing is held back for the paid tier.
Queries
- Always bind parameters. Never concatenate a value into SQL — it breaks on an apostrophe and is injectable in general.
- Select the columns you need, not
SELECT *. - Index anything you filter, join or sort by.
- Paginate with
LIMIT/OFFSET; never load an unbounded table into memory.
Writes
- Wrap bulk writes in
withTransactionAsync. Row-by-row inserts are orders of magnitude slower because each is its own disk sync. - Set
PRAGMA journal_mode = WALat open, or concurrent access produces "database is locked".
Migrations
- Ordered, idempotent, forward-only, tracked with
PRAGMA user_version. - Never
DROPor rewrite a column in an upgrade path — that is a user's real data on a device you cannot inspect or restore. - A migration must run cleanly from the oldest supported app version, not just from the current schema on your machine.
Access
- All SQL lives in
src/services/database/. Components and hooks never write queries. - Open the database once and reuse the handle.
Never
- Never store credentials, tokens or personal data here — the file is not encrypted. Use secure storage.
- Never assume the local database survives reinstall.
- Never query inside a render.
6 formats, one per tool
Each tab is the file that tool actually reads, at the path it actually looks in. Knowing where each one looks is most of the work of supporting it.
---
description: SQLite conventions
globs: ["src/services/database/**", "src/services/**/*.sql"]
alwaysApply: false
---
# SQLite
## Queries
- **Always bind parameters.** Never concatenate a value into SQL — it breaks on
an apostrophe and is injectable in general.
- Select the columns you need, not `SELECT *`.
- Index anything you filter, join or sort by.
- Paginate with `LIMIT`/`OFFSET`; never load an unbounded table into memory.
## Writes
- Wrap bulk writes in `withTransactionAsync`. Row-by-row inserts are orders of
magnitude slower because each is its own disk sync.
- Set `PRAGMA journal_mode = WAL` at open, or concurrent access produces
"database is locked".
## Migrations
- Ordered, idempotent, **forward-only**, tracked with `PRAGMA user_version`.
- Never `DROP` or rewrite a column in an upgrade path — that is a user's real
data on a device you cannot inspect or restore.
- A migration must run cleanly from the oldest supported app version, not just
from the current schema on your machine.
## Access
- All SQL lives in `src/services/database/`. Components and hooks never write
queries.
- Open the database once and reuse the handle.
## Never
- Never store credentials, tokens or personal data here — the file is not
encrypted. Use secure storage.
- Never assume the local database survives reinstall.
- Never query inside a render.
Hand-written by the module author, frontmatter and all. It is the source the four derived formats are rendered from, so a correction lands here first.
What else this module writes
The rule is one file of several. Selecting SQLite contributes all of this too — merged with every other module you pick, with conflicts resolved rather than duplicated.
Advisory history
Every time this rule turned out to be wrong, and what we did about it.
This rule has been accurate since it was published. That is a fact about the rule, not a promise about the future — which is the whole reason this section exists.
Pro tells you the day a correction lands that affects a repo you actually have.
See what Pro adds →Rules people add alongside this one
The wizard picks the rest of the stack with you, writes all 6 formats, and leaves a manifest so check can tell you when any of it drifts.