Drizzle ORM Queries and SQL
Pack:
drizzle-ormSource:drizzle-orm/drizzle-orm-queries-and-sql/SKILL.mdUse this skill when the task is primarily about querying, mutating, or composing SQL with Drizzle’s query APIs.
- SQL-like CRUD builders
- relation-aware query APIs
- joins, filters, aggregations, pagination, and set operations
sqlexpressions and query utilities- transactions, savepoints, batch, cache, and read replicas
- dynamic query-building helpers
Routing cues
Section titled “Routing cues”select,insert,update,delete, joins, operators,sql,count, pagination, transactions, batch, or cache -> use this skill- schema declaration, indexes,
relations,defineRelations, views, or custom types -> usedrizzle-orm-schema-and-relations - runtime driver setup or provider-specific DB client initialization -> use
drizzle-orm-drivers-and-runtimes drizzle-kitcommands or migration strategy decisions -> usedrizzle-orm-migrations-and-drizzle-kit- validation schemas, seed, GraphQL, or lint rules -> use
drizzle-orm-ecosystem-and-extensions
Default path
Section titled “Default path”- Read references/query-patterns.md first.
- If the task touches transactions, batch, cache, read replicas, or query-builder reuse, read references/advanced-query-features.md.
- Keep query style consistent with the surface already used in the repo: SQL-like builders,
db.query, or beta-linedb._query. - Use raw SQL escape hatches deliberately and keep types honest.
When to deviate
Section titled “When to deviate”- Use
.$dynamic()only when the query builder actually needs multiple conditional passes. - Reach for raw
sqlonly when the normal builder cannot express the query clearly. - Move to schema skill when the real problem is model ownership rather than query composition.
Quick example
Section titled “Quick example”import { eq } from "drizzle-orm";
const rows = await db .select() .from(users) .where(eq(users.id, 1));Guardrails
Section titled “Guardrails”- Drizzle deliberately mirrors SQL structure, so most builder methods can only be called once unless you opt into
.$dynamic(). sql<T>only sets the expected TypeScript type. It does not cast runtime values. Use.mapWith()or query helpers when you need runtime conversion.- Count and aggregation results can come back as strings in PostgreSQL or MySQL driver paths. Cast or map them intentionally.
- Distinguish stable
db.query.<table>relational work from beta-linedb._query.<table>APIs before refactoring. - Cache is explicit by default. Do not assume hidden caching or invalidation.
- Batch support is driver-specific in the docs. Check compatibility before using
db.batch(...).
- mixing stable
db.queryand betadb._queryassumptions casually - assuming
sql<T>changes runtime casting behavior - using
.$dynamic()before simpler query composition is exhausted - assuming batch and cache semantics are universal across drivers
Verification checklist
Section titled “Verification checklist”- the query surface matches the version line already in use
- raw SQL is justified and typed honestly
- count and aggregation result types are handled intentionally
- transaction, batch, cache, or replica behavior is driver-aware
- schema or migration concerns are routed out when they dominate
Canonical APIs and helpers
Section titled “Canonical APIs and helpers”db.select,db.insert,db.update,db.deletedb.query.<table>db._query.<table>sql- filter operators like
eq,and,or,inArray .$dynamic()db.transaction(...)db.batch(...)
Maintenance
Section titled “Maintenance”- Snapshot date: 2026-03-10
- Package snapshot:
drizzle-orm@0.45.1latest, beta tag1.0.0-beta.16-ea816b6