Covers Drizzle ORM schema, migrations, queries, relations, transactions, and type inference for SQLite backends (better-sqlite3, Turso, D1, expo-sqlite).
How this skill is triggered — by the user, by Claude, or both
Slash command
/pproenca-dot-skills-1:drizzle-sqliteThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Library-reference skill for Drizzle ORM with SQLite-family backends. 45 rules across 8 categories, ordered by execution-lifecycle impact: schema → migrations → query → relations → transactions → performance → connection → types.
Library-reference skill for Drizzle ORM with SQLite-family backends. 45 rules across 8 categories, ordered by execution-lifecycle impact: schema → migrations → query → relations → transactions → performance → connection → types.
Reference these guidelines when:
sqliteTable schemas — choosing column types, primary keys, indexes, foreign keysdrizzle-kit generate / migrate / push, or hand-editing a migration SQL filedb.select(), db.insert(), db.update(), db.delete()db.query.* and the relational query builderdb.transaction() or db.batch() (libsql/Turso/D1).prepare() + sql.placeholder() or covering indexes$inferSelect, drizzle-zod, JSON shapes)The skill is not specific to one driver — it covers behavior shared across better-sqlite3, libsql, bun:sqlite, expo-sqlite, op-sqlite, and Cloudflare D1, calling out driver-specific deviations where they exist.
SQLite is unusual among production databases:
INTEGER, REAL, TEXT, BLOB, or NULL — Drizzle column modes encode the rest.ALTER TABLE. Only RENAME COLUMN, ADD COLUMN, DROP COLUMN. Type changes and constraint additions need a table rebuild.PRAGMA foreign_keys = ON is per-connection and not persistent.Many rules in this skill exist because Drizzle's API abstracts over PostgreSQL/MySQL/SQLite uniformly — but the underlying SQLite engine has constraints that show up at runtime if you treat it like Postgres.
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Schema Definition | CRITICAL | schema- |
| 2 | Migrations & Drizzle Kit | CRITICAL | migrate- |
| 3 | Query Building | HIGH | query- |
| 4 | Relations | HIGH | rel- |
| 5 | Transactions & Batching | MEDIUM-HIGH | tx- |
| 6 | Prepared Statements & Hot Paths | MEDIUM-HIGH | perf- |
| 7 | Connection & Driver Setup | MEDIUM | conn- |
| 8 | Type Inference | MEDIUM | types- |
schema-integer-for-booleans — Use integer({ mode: 'boolean' }) so the inferred type is boolean, not 0 | 1schema-timestamp-mode-for-dates — Store dates as integer({ mode: 'timestamp_ms' }), not textschema-always-primary-key — Declare an explicit PK (single or composite); don't rely on hidden rowidschema-foreign-keys-with-actions — Specify onDelete/onUpdate on every .references()schema-index-foreign-keys-and-lookups — Index FK columns and frequent WHEREs — SQLite does not auto-index FKsschema-text-json-not-blob-json — Use text({ mode: 'json' }) so json_extract and JSON-path indexes workschema-unique-constraints-for-natural-keys — .unique() for email/slug/externalId so onConflict has a targetmigrate-generate-not-push-in-prod — Use generate + migrate; push drops columns it can't reconcilemigrate-explicit-renames — Answer the rename prompt — defaults treat renames as drop+addmigrate-config-dialect-and-out — Define drizzle.config.ts so commands work without flagsmigrate-apply-with-migrator — Apply via drizzle-kit migrate or the driver migrator module, not raw SQLmigrate-data-backfill-as-custom-sql — Hand-edit migration SQL to backfill atomically with the DDLmigrate-commit-migrations-to-git — Commit drizzle/ SQL and drizzle/meta/ snapshots — both are requiredquery-select-columns-not-star — Project to the columns you need with db.select({ ... })query-avoid-n-plus-one-with-inarray — Replace looped queries with inArray()query-always-limit-listings — Every listing query needs .limit() (and ideally a cursor)query-bind-parameters-not-concat — Use eq() / sql template — never string-concat valuesquery-upsert-with-onconflict — Atomic upserts via .onConflictDoUpdate(), not select-then-writequery-returning-instead-of-reselect — .returning() on insert/update/delete saves a round tripquery-toSQL-and-explain — Inspect generated SQL and EXPLAIN QUERY PLAN on hot pathsrel-declare-relations-for-rqb — relations() declarations unlock db.query.* and withrel-prefer-with-over-manual-joins — with for nested fetches; manual joins lose typing and add coderel-partial-columns-in-with — columns: { ... } inside with to limit payload and avoid leaksrel-filter-with-where-inside-with — Push related-row filters into with.where, not into JSrel-leftjoin-for-flat-aggregates — Drop to leftJoin + groupBy when you need aggregatestx-wrap-multi-statement-writes — Wrap related writes in db.transaction() for atomicity + throughputtx-batch-for-libsql-roundtrips — db.batch() on libsql/Turso/D1 collapses N round trips into 1tx-no-network-io-inside-transaction — No awaited HTTP / FS / Stripe calls inside a transactiontx-handle-busy-with-retry — Bounded retries on SQLITE_BUSY — only on transient errorstx-single-writer-no-parallel-writes — Promise.all of writes contends; serialize themperf-prepare-hot-paths — .prepare() + sql.placeholder() for queries running on every requestperf-bulk-insert-multi-row-values — One values([...rows]) instead of N looped insertsperf-avoid-count-star-on-large-tables — Counter rows or keyset pagination instead of count(*)perf-keyset-not-offset-for-deep-pages — Keyset pagination keeps cost constant across pagesperf-covering-index-for-hot-queries — Cover the projected columns so the planner skips the row readconn-enable-wal — journal_mode = WAL for concurrent reads + one writerconn-set-busy-timeout — busy_timeout = 5000 turns contention into a waitconn-foreign-keys-pragma — foreign_keys = ON per connection — off by defaultconn-singleton-client — Module-scope singleton; never per-request constructionconn-pick-driver-deliberately — Sync vs async vs HTTP — choose by deployment targettypes-infer-select-insert — Derive row types with $inferSelect / $inferInserttypes-narrow-json-with-dollartype — .$type<Shape>() to escape unknown on JSON columnstypes-getTableColumns-for-reuse — Share projections via getTableColumns() + spreadtypes-drizzle-zod-for-runtime-validation — createInsertSchema(table) derives a Zod validator from the schematypes-bigint-mode-for-large-integers — mode: 'bigint' for IDs over Number.MAX_SAFE_INTEGERRead the relevant category overview in references/_sections.md, then the specific rule files for detailed explanations and code examples. Each rule has incorrect-vs-correct examples — apply the correct pattern to the code under review.
For complex changes (schema redesign, migration strategy, performance work), read all rules in the affected category before deciding.
| File | Description |
|---|---|
| references/_sections.md | Category definitions and ordering |
| assets/templates/_template.md | Template for new rules |
| metadata.json | Version and reference information |
effect-ts — When the application is Effect-based; Drizzle integrates via Effect.tryPromise.nextjs-bundle-optimizer — For Next.js apps reaching for SQLite as the data layer.better-auth — Often paired with Drizzle SQLite for auth tables; see better-auth-scaffold for table generation.npx claudepluginhub joshuarweaver/cascade-code-general-misc-1 --plugin pproenca-dot-skills-1Scaffolds Drizzle ORM + SQLite boilerplate: config, singleton client, per-table schema with relations, CRUD repositories, and drizzle-zod validators for three SQLite drivers.
Designs type-safe database schemas and queries using Drizzle ORM, including migrations, relational queries, and serverless integrations.
Provides type-safe SQL with Drizzle ORM for defining schemas, writing queries, setting relations, and running migrations across PostgreSQL, MySQL, SQLite, Cloudflare D1, and Durable Objects.