SQLite

sqliteDatabase37 lines

On-device SQL storage for offline-first data and local caching.

Current — as published in v1.3.2. Written by hand for SQLite, not generated.

The rule

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.

As each tool receives it

The same guidance, in the shape each editor reads. Cursor and Claude Code are written separately by hand; the rest are derived from the Cursor rule.

.cursor/rules/sqlite.mdchand-written for this tool
.cursor/rules/sqlite.mdc
---
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.

What else this module writes

Selecting SQLite contributes more than a rule file — all of it merged with every other module you pick, with conflicts resolved rather than duplicated.

Dependencies
expo-sqlite^57.0.0
Folders
src/services/database/migrations/

Related

Put this in your repo

Adds SQLite to a project this tool generated, without starting over. Files you have hand-edited are left alone.