| name | sqlite |
| description | Expert SQLite guidance for modern full-stack and AI projects. Use when user mentions sqlite, sqlite3, bun:sqlite, better-sqlite3, aiosqlite, libsql, Turso, Cloudflare D1, WAL mode, FTS5, sqlite-vec, or asks about local/embedded databases, schema design, SQLite migrations with Drizzle or raw SQL, performance tuning, backup/restore, and building stateful CLI with SQLite. |
SQLite Expert Skill
SQLite is a self-contained, serverless, zero-configuration SQL database engine. It's the right
choice for local tools, CLI apps, AI agents, edge deployments (Cloudflare D1, Turso), and
embedded databases in SaaS products.
Quick Decision Tree
What do you need?
├── TypeScript + Bun project? → references/TYPESCRIPT_DRIZZLE.md
├── Python / FastAPI project? → references/PYTHON.md
├── Performance, WAL, PRAGMA tuning? → references/PERFORMANCE.md
├── Full-text search (FTS5)? → references/FTS5_VECTOR.md §FTS5
├── AI vector embeddings? → references/FTS5_VECTOR.md §sqlite-vec
├── Cloud SQLite (Turso / D1)? → references/CLOUD.md
├── Building a stateful CLI Skill? → references/CLI_SKILL_PATTERN.md
├── Schema design & normalization? → This file §Schema Design
└── Backup, vacuum, integrity? → This file §Maintenance
Read the referenced file ONLY when relevant. Don't load all references at once.
Core Concepts
Driver Selection
| Runtime | Recommended Driver | Notes |
|---|
| Bun | bun:sqlite (built-in) | Zero-dep, fastest |
| Bun + ORM | drizzle-orm/bun-sqlite | Type-safe, migrations |
| Node.js | better-sqlite3 | Sync, very fast |
| Node.js async | @libsql/client | For Turso/libsql |
| Python sync | sqlite3 (stdlib) | Zero-dep |
| Python async | aiosqlite | asyncio-native |
| Edge / CF | Cloudflare D1 binding | Workers only |
Always-On PRAGMAs
Run these immediately after opening any connection:
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA foreign_keys = ON;
PRAGMA cache_size = -64000;
PRAGMA temp_store = MEMORY;
PRAGMA mmap_size = 268435456;
⚠️ WAL is a file-level setting — it persists across connections. Set once, check after.
Schema Design
Naming Conventions
CREATE TABLE user_sessions (...);
created_at, updated_at, deleted_at
id INTEGER PRIMARY KEY AUTOINCREMENT
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE
Type Affinity Rules
SQLite has flexible typing — use these mappings:
| Concept | SQLite Type | Notes |
|---|
| Auto-increment PK | INTEGER PRIMARY KEY | Also the rowid alias |
| String | TEXT | UTF-8 by default |
| Boolean | INTEGER (0/1) | No native bool |
| Float | REAL | 8-byte IEEE 754 |
| Timestamps | INTEGER (unix) or TEXT (ISO8601) | Pick one, be consistent |
| JSON | TEXT | Use json_extract() / -> operator |
| Binary | BLOB | Images, embeddings |
| UUID | TEXT (36 chars) or BLOB (16 bytes) | BLOB is 60% smaller |
Timestamp Pattern (Recommended)
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
Use triggers for updated_at:
CREATE TRIGGER set_updated_at
AFTER UPDATE ON my_table
BEGIN
UPDATE my_table SET updated_at = unixepoch() WHERE id = NEW.id;
END;
JSON Columns
SQLite 3.38+ supports the -> and ->> operators:
INSERT INTO events (payload) VALUES ('{"type":"click","x":100}');
SELECT payload -> '$.type' FROM events;
SELECT payload ->> '$.type' FROM events;
CREATE INDEX idx_events_type ON events(payload ->> '$.type');
Soft Delete Pattern
deleted_at INTEGER DEFAULT NULL
SELECT * FROM users WHERE deleted_at IS NULL;
CREATE INDEX idx_users_active ON users(email) WHERE deleted_at IS NULL;
Indexing Strategy
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_sessions_user_created ON sessions(user_id, created_at);
CREATE INDEX idx_jobs_pending ON jobs(created_at) WHERE status = 'pending';
CREATE INDEX idx_users_email_name ON users(email, name);
EXPLAIN QUERY PLAN SELECT * FROM posts WHERE user_id = 1 ORDER BY created_at DESC;
Index anti-patterns:
- Don't index every column — writes get slower
- Avoid indexing low-cardinality columns (boolean, enum with few values)
- Composite index column order matters:
(a, b) helps WHERE a=? AND b=? and WHERE a=?, but NOT WHERE b=?
Transactions
BEGIN;
INSERT INTO logs VALUES (...);
INSERT INTO logs VALUES (...);
COMMIT;
SAVEPOINT sp1;
ROLLBACK TO sp1;
BEGIN IMMEDIATE;
Bulk insert pattern (10-100x faster than individual inserts):
const insert = db.prepare('INSERT INTO items (name, value) VALUES (?, ?)');
const insertMany = db.transaction((items) => {
for (const item of items) insert.run(item.name, item.value);
});
insertMany(myArray);
Maintenance
Vacuum & Analyze
VACUUM;
PRAGMA incremental_vacuum(100);
ANALYZE;
PRAGMA integrity_check;
PRAGMA quick_check;
Backup
sqlite3 app.db ".backup backup_$(date +%Y%m%d).db"
sqlite3 app.db "VACUUM INTO 'backup.db';"
import sqlite3
src = sqlite3.connect('app.db')
dst = sqlite3.connect('backup.db')
src.backup(dst, pages=100)
dst.close(); src.close()
File Size Check
SELECT
page_count * page_size AS total_bytes,
freelist_count * page_size AS free_bytes,
ROUND(freelist_count * 100.0 / page_count, 1) AS fragmentation_pct
FROM pragma_page_count(), pragma_page_size(), pragma_freelist_count();
Common Pitfalls
| Pitfall | Fix |
|---|
SQLITE_BUSY errors | Enable WAL mode; use BEGIN IMMEDIATE for writes |
| Slow bulk inserts | Wrap in explicit BEGIN/COMMIT transaction |
| No FK enforcement | Run PRAGMA foreign_keys = ON on every connection |
| Boolean confusion | Store as INTEGER 0/1; never 'true'/'false' |
| Timestamp inconsistency | Pick unix (INTEGER) or ISO8601 (TEXT), never mix |
| File locking on network drives | Never use SQLite on NFS/SMB — copy locally first |
| WAL file growing | Run PRAGMA wal_checkpoint(TRUNCATE) periodically |
Reference Files
| File | When to Read |
|---|
references/TYPESCRIPT_DRIZZLE.md | TypeScript + Bun + Drizzle ORM setup, migrations, relations |
references/PYTHON.md | Python sqlite3, aiosqlite, FastAPI integration |
references/PERFORMANCE.md | Deep PRAGMA tuning, profiling, EXPLAIN QUERY PLAN |
references/FTS5_VECTOR.md | FTS5 full-text search + sqlite-vec AI embeddings |
references/CLOUD.md | Turso (libsql), Cloudflare D1, multi-region |
references/CLI_SKILL_PATTERN.md | CLI + SQLite stateful Skill pattern |