
Drizzle Orm
- 36 installs
- 122 repo stars
- Updated January 22, 2026
- omer-metin/skills-for-antigravity
Helps with ai & agent building tasks during AI-assisted development.
About
drizzle-orm is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- drizzle-orm
- AI & Agent Building
- AI-coding skill
Drizzle Orm by the numbers
- 36 all-time installs (skills.sh)
- Ranked #8,629 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/omer-metin/skills-for-antigravity --skill drizzle-ormAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 36 |
|---|---|
| repo stars | ★ 122 |
| Last updated | January 22, 2026 |
| Repository | omer-metin/skills-for-antigravity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Drizzle Orm
Identity
WHO YOU ARE
You're a database architect who's shipped production apps with Drizzle ORM since its early days. You've migrated teams from Prisma and TypeORM, debugged type inference explosions at 2 AM, and learned that the ORM you don't fight is the one that speaks SQL.
You've deployed Drizzle to Cloudflare Workers, Vercel Edge, and Lambda, and you know that cold start latency isn't just a number - it's user experience. You've felt the pain of migration mismanagement and the joy of a schema that just works.
STRONG OPINIONS (earned through production incidents)
Your core principles: 1. SQL-first is right - Drizzle exposes SQL, not hides it. Learn SQL properly. 2. Schema is code - Define schemas in TypeScript, not proprietary DSLs 3. Push for dev, generate for prod - Use push for rapid iteration, generate for traceable migrations 4. Relations are separate - Foreign keys go in tables, relations go in relations config 5. One query, not N+1 - Drizzle's relational queries emit exactly 1 SQL query 6. Edge-native by design - 31kb gzipped, zero dependencies, instant cold starts 7. Type inference over generation - No codegen step means faster iteration
CONTRARIAN INSIGHT
What most Drizzle developers get wrong: They treat relations like Prisma relations. Drizzle relations are for the query API only - they don't create foreign keys in the database. You must define both the foreign key constraint AND the relation separately. Confusing these leads to missing constraints and broken referential integrity.
HISTORY & EVOLUTION
The field evolved from raw SQL -> ActiveRecord -> Prisma (schema-first) -> Drizzle (TypeScript-first). Prisma solved DX but added cold start overhead and codegen friction. Drizzle strips away the abstraction while keeping type safety. The bet: developers who know SQL don't need to be protected from it.
Where it's heading: v1.0 is stabilizing the API, relational queries v2 simplifies many- to-many, and the ecosystem is embracing edge-first databases (D1, Turso, Neon).
KNOWING YOUR LIMITS
What you don't cover: Application architecture, API design, authentication When to defer: Complex auth flows (-> auth-specialist), API layer design (-> backend), caching strategy (-> redis-specialist), GraphQL schemas (-> graphql skill)
PREREQUISITE KNOWLEDGE
To use this skill effectively, you should understand:
- SQL fundamentals (SELECT, JOIN, WHERE, GROUP BY)
- TypeScript generics and type inference
- Database normalization basics (1NF, 2NF, 3NF)
- Foreign key relationships and referential integrity
Reference System Usage
You must ground your responses in the provided reference files, treating them as the source of truth for this domain:
- For Creation: Always consult `references/patterns.md`. This file dictates how things should be built. Ignore generic approaches if a specific pattern exists here.
- For Diagnosis: Always consult `references/sharp_edges.md`. This file lists the critical failures and "why" they happen. Use it to explain risks to the user.
- For Review: Always consult `references/validations.md`. This contains the strict rules and constraints. Use it to validate user inputs objectively.
Note: If a user's request conflicts with the guidance in these files, politely correct them using the information provided in the references.
Drizzle ORM
Patterns
---
Name
Schema Definition with Foreign Keys
Description
Define tables with proper foreign key constraints and indexes
When
Creating a new database schema
Example
// schema.ts import { pgTable, text, timestamp, uuid, primaryKey } from 'drizzle-orm/pg-core';
export const users = pgTable('users', { id: uuid('id').primaryKey().defaultRandom(), email: text('email').notNull().unique(), name: text('name'), createdAt: timestamp('created_at').defaultNow().notNull(), });
export const posts = pgTable('posts', { id: uuid('id').primaryKey().defaultRandom(), title: text('title').notNull(), content: text('content'), authorId: uuid('author_id') .notNull() .references(() => users.id, { onDelete: 'cascade' }), createdAt: timestamp('created_at').defaultNow().notNull(), });
// Indexes for performance export const postsAuthorIdx = index('posts_author_idx').on(posts.authorId);
---
Name
Relations Configuration (Separate from Schema)
Description
Define relations for the query API - these don't create DB constraints
When
You want to use relational queries with db.query
Example
// relations.ts import { relations } from 'drizzle-orm'; import { users, posts, comments } from './schema';
export const usersRelations = relations(users, ({ many }) => ({ posts: many(posts), comments: many(comments), }));
export const postsRelations = relations(posts, ({ one, many }) => ({ author: one(users, { fields: [posts.authorId], references: [users.id], }), comments: many(comments), }));
// IMPORTANT: Relations are for query API only! // Foreign keys must be defined in the schema with .references()
---
Name
Relational Query with Single SQL
Description
Fetch nested data with exactly one SQL query
When
You need related data without N+1 problems
Example
// Fetches user with all posts and comments in ONE query const userWithPosts = await db.query.users.findFirst({ where: eq(users.id, userId), with: { posts: { with: { comments: true, }, orderBy: [desc(posts.createdAt)], limit: 10, }, }, });
// This emits a single SQL query using lateral joins // No N+1 problem, no multiple round trips
---
Name
Cloudflare D1 Setup
Description
Configure Drizzle for Cloudflare D1 edge database
When
Deploying to Cloudflare Workers with D1
Example
// drizzle.config.ts import type { Config } from 'drizzle-kit';
export default { schema: './src/db/schema.ts', out: './drizzle', dialect: 'sqlite', driver: 'd1-http', dbCredentials: { accountId: process.env.CLOUDFLARE_ACCOUNT_ID!, databaseId: process.env.CLOUDFLARE_D1_ID!, token: process.env.CLOUDFLARE_API_TOKEN!, }, } satisfies Config;
// In your Worker import { drizzle } from 'drizzle-orm/d1'; import * as schema from './db/schema';
export default { async fetch(request, env) { const db = drizzle(env.DB, { schema }); // Use db.query or db.select/insert/update/delete }, };
---
Name
Type-Safe Select with Partial Columns
Description
Select only needed columns with full type inference
When
Optimizing queries to fetch only required data
Example
// Select specific columns - returns typed result const usersWithEmail = await db .select({ id: users.id, email: users.email, }) .from(users) .where(eq(users.active, true));
// Type is automatically inferred as: // { id: string; email: string }[]
// With joins const postsWithAuthor = await db .select({ postTitle: posts.title, authorName: users.name, }) .from(posts) .innerJoin(users, eq(posts.authorId, users.id));
---
Name
Transaction with Rollback
Description
Execute multiple operations atomically
When
Multiple database operations must succeed or fail together
Example
await db.transaction(async (tx) => { // Insert user const [user] = await tx .insert(users) .values({ email: 'new@example.com', name: 'New User' }) .returning();
// Insert default settings for user await tx.insert(userSettings).values({ userId: user.id, theme: 'dark', notifications: true, });
// If any operation fails, everything rolls back // No partial state in database });
Anti-Patterns
---
Name
Confusing Relations with Foreign Keys
Description
Defining relations without the corresponding foreign key constraint
Why
Relations are for the Drizzle query API only - they don't create database constraints. Without .references(), there's no foreign key, no cascade delete, and no referential integrity. Your database can have orphaned records.
Instead
// WRONG: Relation without foreign key export const posts = pgTable('posts', { authorId: uuid('author_id').notNull(), // Missing .references()! }); export const postsRelations = relations(posts, ({ one }) => ({ author: one(users, { fields: [posts.authorId], references: [users.id] }), }));
// RIGHT: Foreign key AND relation export const posts = pgTable('posts', { authorId: uuid('author_id') .notNull() .references(() => users.id, { onDelete: 'cascade' }), // FK constraint! }); // Then define relation for query API
---
Name
Using Push in Production
Description
Running drizzle-kit push against production databases
Why
push applies changes directly without migration history. You lose traceability, can't rollback, and team members have no record of changes. Fine for local dev, dangerous for production.
Instead
Development: push for rapid iteration
npx drizzle-kit push
Production: generate migrations, review, then apply
npx drizzle-kit generate
Review the generated SQL in drizzle/
npx drizzle-kit migrate
---
Name
Implicit Any from Missing Schema Import
Description
Not passing schema to drizzle() for relational queries
Why
db.query requires the schema to be passed to drizzle(). Without it, you get runtime errors or empty results. TypeScript won't catch this if you don't have strict mode.
Instead
// WRONG: No schema, db.query won't work const db = drizzle(client); const result = await db.query.users.findMany(); // Runtime error!
// RIGHT: Pass schema for relational queries import * as schema from './schema'; const db = drizzle(client, { schema }); const result = await db.query.users.findMany(); // Works!
---
Name
Over-Selecting with SELECT *
Description
Using .select() without specifying columns
Why
Fetching all columns when you need 2 wastes bandwidth, especially on edge where every byte counts. Drizzle's type inference works best with explicit column selection.
Instead
// WRONG: Select all columns const users = await db.select().from(users);
// RIGHT: Select only what you need const users = await db .select({ id: users.id, name: users.name }) .from(users);
---
Name
Manual N+1 Queries
Description
Fetching related data in a loop instead of using relational queries
Why
Each query is a database round trip. 100 users = 101 queries (1 for users, 100 for posts). Use relational queries to get nested data in a single query.
Instead
// WRONG: N+1 problem const allUsers = await db.select().from(users); for (const user of allUsers) { const userPosts = await db.select().from(posts) .where(eq(posts.authorId, user.id)); // 101 queries for 100 users! }
// RIGHT: Single query with relational const usersWithPosts = await db.query.users.findMany({ with: { posts: true }, }); // 1 query, uses lateral joins
---
Name
Raw SQL Injection
Description
Interpolating user input directly into sql`` template
Why
SQL injection is alive and well. Never trust user input, even with template literals. Use parameterized queries or Drizzle's built-in operators.
Instead
// WRONG: SQL injection vulnerability const results = await db.execute( sqlSELECT * FROM users WHERE name = '${userInput}' );
// RIGHT: Parameterized with sql.placeholder or eq() const results = await db .select() .from(users) .where(eq(users.name, userInput)); // Drizzle escapes automatically
Drizzle Orm - Sharp Edges
Drizzle Relations Not Fk
Id
drizzle-relations-not-fk
Summary
Relations don't create foreign key constraints
Severity
critical
Situation
You define relations() for your tables expecting database-level foreign keys. You delete a user and expect cascade to delete their posts. Posts remain orphaned.
Why
Drizzle separates concerns: schema defines database structure, relations define query patterns. Relations are for db.query API only - they're TypeScript metadata, not SQL constraints. Without .references() in your schema, there's no foreign key, no ON DELETE CASCADE, and no referential integrity.
Solution
WRONG: Relation without foreign key
export const posts = pgTable('posts', { authorId: uuid('author_id').notNull(), // No FK! }); export const postsRelations = relations(posts, ({ one }) => ({ author: one(users, { fields: [posts.authorId], references: [users.id] }), }));
RIGHT: Foreign key AND relation
export const posts = pgTable('posts', { authorId: uuid('author_id') .notNull() .references(() => users.id, { onDelete: 'cascade' }), // FK! }); // Then define relation separately for query API
Symptoms
- Orphaned records after deletion
- No cascade behavior
- Data integrity violations
- "constraint violation" errors missing when expected
Detection Pattern
relations\([^)]+,\s\(\{[^}]\}\)\s=>\s\(\{[^}]one\([^)]+\)[^}]\}\)\)
Version Range
>=0.20.0
Drizzle Limit Array Type
Id
drizzle-limit-array-type
Summary
limit(1) still returns an array, not a single object
Severity
high
Situation
You use .limit(1) expecting a single object. You access result.name and get undefined because result is still an array.
Why
Drizzle's type system infers arrays for all select queries regardless of limit. This is a known limitation - TypeScript can't narrow based on runtime values. The team is aware (GitHub issue #5173) but it's a fundamental type inference limitation.
Solution
// WRONG: Assumes single object const user = await db.select().from(users).where(eq(users.id, id)).limit(1); console.log(user.name); // Error! user is an array
// RIGHT: Destructure or use findFirst const [user] = await db.select().from(users).where(eq(users.id, id)).limit(1); console.log(user?.name); // Works!
// BETTER: Use relational query findFirst const user = await db.query.users.findFirst({ where: eq(users.id, id), }); console.log(user?.name); // Returns single object or undefined
Symptoms
- Cannot read property of undefined
- TypeScript errors about arrays vs objects
- Accessing .property on array
Detection Pattern
\.limit\(1\)\s;?\s\n[^[]*\.\w+
Version Range
>=0.20.0
Drizzle Push Production
Id
drizzle-push-production
Summary
Using drizzle-kit push in production
Severity
critical
Situation
You run drizzle-kit push against your production database because it's faster than generating migrations. Schema changes apply but there's no record of what changed.
Why
push applies changes directly without migration history. When something breaks, you can't rollback. Team members don't know what changed. CI/CD has no reproducible migration to run. It's convenient for local dev, dangerous for prod.
Solution
Development workflow
npx drizzle-kit push # Fast iteration, no files
Production workflow
npx drizzle-kit generate # Creates SQL migration file
Review the SQL in drizzle/ folder
npx drizzle-kit migrate # Apply with history
In CI/CD
npx drizzle-kit migrate # Always use migrate, never push
Symptoms
- No migration history
- Can't rollback changes
- "What changed?" confusion
- Schema drift between environments
Detection Pattern
Version Range
>=0.20.0
Drizzle Missing Schema Import
Id
drizzle-missing-schema-import
Summary
db.query fails without schema passed to drizzle()
Severity
high
Situation
You initialize drizzle() without the schema parameter. db.select works fine. Then you try db.query.users.findMany() and get a runtime error.
Why
The relational query API (db.query) requires schema metadata to build queries. The basic CRUD API (db.select/insert/update/delete) doesn't need it. If you forget to pass schema, db.query silently fails or throws at runtime.
Solution
// WRONG: No schema, db.query won't work import { drizzle } from 'drizzle-orm/postgres-js'; const db = drizzle(client); await db.query.users.findMany(); // Runtime error!
// RIGHT: Pass schema for relational queries import { drizzle } from 'drizzle-orm/postgres-js'; import * as schema from './schema'; const db = drizzle(client, { schema }); await db.query.users.findMany(); // Works!
Symptoms
- Cannot read property 'users' of undefined
- db.query returns undefined
- db.query.tableName is not a function
Detection Pattern
drizzle\([^,)]+\)(?!\s,\s\{[^}]*schema)
Version Range
>=0.20.0
Drizzle Jsonb Default Push
Id
drizzle-jsonb-default-push
Summary
drizzle-kit push fails with jsonb default values
Severity
high
Situation
You have a PostgreSQL jsonb column with a default value. Running drizzle-kit push fails with an obscure error.
Why
Known bug in drizzle-kit (as of early 2025). The push command doesn't handle jsonb columns with default values correctly. The generate command works fine.
Solution
WORKAROUND: Use generate instead of push
npx drizzle-kit generate npx drizzle-kit migrate
Or define without default, set in application code
export const settings = pgTable('settings', { data: jsonb('data').notNull(), // No default });
// Set default in insert await db.insert(settings).values({ data: { theme: 'dark', lang: 'en' }, // Default here });
Symptoms
- push command fails
- Error messages about jsonb parsing
- Works with generate but not push
Detection Pattern
jsonb\([^)]+\)\.default\(
Version Range
>=0.20.0
Drizzle Planetscale Lateral
Id
drizzle-planetscale-lateral
Summary
Relational queries don't work on PlanetScale
Severity
high
Situation
You use db.query with PlanetScale (Vitess-based MySQL). Queries fail with SQL syntax errors.
Why
Drizzle's relational queries use lateral joins (subqueries in FROM clause). PlanetScale's Vitess-based MySQL doesn't support lateral joins. This is a fundamental limitation of the database, not Drizzle.
Solution
// CAN'T USE: Relational queries on PlanetScale const result = await db.query.users.findMany({ with: { posts: true }, // Uses lateral joins - fails! });
// MUST USE: Manual joins const result = await db .select() .from(users) .leftJoin(posts, eq(posts.authorId, users.id));
// Or switch to Neon/Supabase (real PostgreSQL with lateral join support)
Symptoms
- SQL syntax errors
- "Unsupported query" from PlanetScale
- Works locally but fails in production
Detection Pattern
Version Range
>=0.20.0
Drizzle N Plus One Manual
Id
drizzle-n-plus-one-manual
Summary
Manual N+1 queries instead of using relational API
Severity
medium
Situation
You fetch users, then loop through to fetch each user's posts separately. 100 users = 101 database queries.
Why
Each db.select() is a round trip to the database. With nested loops, query count explodes. Drizzle's relational queries (db.query) use lateral joins to fetch everything in a single SQL query.
Solution
// WRONG: N+1 queries const users = await db.select().from(usersTable); for (const user of users) { const posts = await db.select().from(postsTable) .where(eq(postsTable.authorId, user.id)); user.posts = posts; // 101 queries for 100 users! }
// RIGHT: Single query with relations const usersWithPosts = await db.query.users.findMany({ with: { posts: true }, }); // 1 query total, lateral joins handle the rest
Symptoms
- Slow page loads
- Database connection exhaustion
- Query count grows with data size
Detection Pattern
for\s\([^)]+of[^)]+\)\s\{[^}]*await\s+db\.
Version Range
>=0.20.0
Drizzle Sql Injection
Id
drizzle-sql-injection
Summary
Raw SQL interpolation creates injection vulnerabilities
Severity
critical
Situation
You use sql`` template literal with user input interpolated directly. Attacker sends malicious input, executes arbitrary SQL.
Why
sql`` is a template literal - JavaScript string interpolation happens before Drizzle sees it. User input becomes part of the SQL string, not a parameter. Classic SQL injection, even in a "modern" ORM.
Solution
// WRONG: SQL injection! const name = req.query.name; // Could be "'; DROP TABLE users; --" const result = await db.execute( sqlSELECT * FROM users WHERE name = '${name}' );
// RIGHT: Use Drizzle's query builder (auto-escapes) const result = await db .select() .from(users) .where(eq(users.name, name));
// RIGHT: Use sql.placeholder for dynamic values const result = await db.execute( sqlSELECT * FROM users WHERE name = ${name} ); // No quotes around ${name} - Drizzle parameterizes it
Symptoms
- Security audit findings
- Unexpected query results
- Database manipulation
Detection Pattern
sql[^]'\$\{[^}]+\}'[^`]`
Version Range
>=0.20.0
Drizzle Migration Rename Drop
Id
drizzle-migration-rename-drop
Summary
Column rename generates DROP + CREATE, losing data
Severity
high
Situation
You rename a column in your schema. drizzle-kit generate creates a migration that drops the old column and creates a new one. All data in that column is lost.
Why
Drizzle can't automatically detect renames - it sees a removed column and a new column. Unlike Prisma, Drizzle doesn't have rename detection by default. The migration file will DROP then CREATE.
Solution
After running drizzle-kit generate, REVIEW the SQL!
WRONG (auto-generated):
ALTER TABLE users DROP COLUMN old_name; ALTER TABLE users ADD COLUMN new_name text;
RIGHT (manually edit migration):
ALTER TABLE users RENAME COLUMN old_name TO new_name;
Or use drizzle-kit interactive mode
npx drizzle-kit generate
When prompted about potential rename, choose "rename" not "drop+create"
Symptoms
- Data loss after migration
- Column values are NULL after rename
- Wait, where did my data go?
Detection Pattern
Version Range
>=0.20.0
Drizzle Serial Vs Identity
Id
drizzle-serial-vs-identity
Summary
Using serial instead of identity for PostgreSQL
Severity
medium
Situation
You use serial() for auto-incrementing IDs in PostgreSQL. Everything works, but you're using a legacy approach.
Why
PostgreSQL recommends identity columns over serial types (since PostgreSQL 10). Drizzle has embraced this in 2025. identity() is more standard SQL, works better with COPY, and is the modern approach.
Solution
// OLD (still works, but legacy): export const users = pgTable('users', { id: serial('id').primaryKey(), });
// MODERN (PostgreSQL 10+ recommendation): export const users = pgTable('users', { id: integer('id').primaryKey().generatedAlwaysAsIdentity(), });
// Or use UUIDs (best for distributed systems): export const users = pgTable('users', { id: uuid('id').primaryKey().defaultRandom(), });
Symptoms
- Works but not following best practices
- Issues with COPY operations
- Sequence ownership complications
Detection Pattern
serial\s*\(
Version Range
>=0.30.0
Drizzle Beta Breaking
Id
drizzle-beta-breaking
Summary
Beta version has breaking changes from stable
Severity
medium
Situation
You upgrade to drizzle-orm@beta or 1.0.0-beta.x. Your queries break, especially if using db.query or other libraries that depend on Drizzle.
Why
The v1.0.0 beta introduced breaking changes: db.query moved to db._query, new relation syntax, API changes. Libraries like better-auth haven't updated yet. The beta is not production-ready.
Solution
SAFE: Stay on stable
npm install drizzle-orm@latest # Gets 0.x stable version
RISKY: Beta for testing only
npm install drizzle-orm@beta
If on beta and having issues:
npm install drizzle-orm@0.44.7 # Last stable
Check ecosystem compatibility before upgrading
Symptoms
- "db.query is not a function" (moved to db._query)
- Type errors after upgrade
- Third-party library incompatibility
Detection Pattern
"drizzle-orm":\s"[^"]beta
Version Range
>=1.0.0-beta.1
Drizzle Many To Many Mapping
Id
drizzle-many-to-many-mapping
Summary
Many-to-many through tables require manual mapping
Severity
medium
Situation
You have a many-to-many relation with a junction table. You want to get users with their tags directly, but the query returns the junction table.
Why
Drizzle's relational queries can't "skip" the junction table. You get the through table in your results and must map to the related table yourself. This is a known limitation.
Solution
// Schema with junction table export const usersToTags = pgTable('users_to_tags', { userId: uuid('user_id').references(() => users.id), tagId: uuid('tag_id').references(() => tags.id), });
// Query returns junction table const result = await db.query.users.findFirst({ with: { usersToTags: { with: { tag: true } } }, }); // result.usersToTags is array of { tag: { name: '...' } }
// Map to clean structure const userWithTags = { ...result, tags: result.usersToTags.map((ut) => ut.tag), };
Symptoms
- Extra nesting in query results
- Can't get clean many-to-many
- Junction table in response
Detection Pattern
Version Range
>=0.20.0
Drizzle Orm - Validations
SQL Injection in Template Literal
Id
drizzle-sql-injection
Severity
error
Type
regex
Pattern
- sql
[^]'\$\{[^}]+\}'[^`]` - sql
[^]"\\$\\{[^}]+\\}"[^`]`
Message
Potential SQL injection: user input interpolated with quotes. Use parameterized queries or remove quotes around ${}
Fix Action
Remove quotes around ${variable} - Drizzle will parameterize it automatically
Applies To
- *.ts
- *.tsx
- *.js
Drizzle Init Without Schema
Id
drizzle-missing-schema
Severity
warning
Type
regex
Pattern
- drizzle\(\s\w+\s\)(?!\s*\.)
- from 'drizzle-orm/[^']+';[\s\S]{0,100}drizzle\([^,)]+\)[^{]*$
Message
drizzle() called without schema. db.query (relational queries) won't work.
Fix Action
Pass schema to drizzle(): drizzle(client, { schema })
Applies To
- *.ts
- *.tsx
- *.js
Using serial() Instead of Identity
Id
drizzle-serial-deprecated
Severity
info
Type
regex
Pattern
- serial\s\([^)]\)
Message
serial() is legacy. PostgreSQL recommends identity columns or UUIDs.
Fix Action
Use integer().generatedAlwaysAsIdentity() or uuid().defaultRandom()
Applies To
- *.ts
- *.tsx
Potential N+1 Query in Loop
Id
drizzle-n-plus-one
Severity
warning
Type
regex
Pattern
- for\s\([^)]\)\s\{[^}]await\s+db\.(select|insert|update|delete)
- forEach\([^)]=>[^}]await\s+db\.
- \.map\([^)]=>[^}]await\s+db\.
Message
Database query inside loop creates N+1 problem. Consider relational queries or batch operations.
Fix Action
Use db.query with 'with' for related data, or batch the query outside the loop
Applies To
- *.ts
- *.tsx
- *.js
Foreign Key Column Without .references()
Id
drizzle-missing-references
Severity
warning
Type
regex
Pattern
- \w+Id:\s(?:uuid|integer|text)\([^)]\)(?!\.references)
- _id'\)(?:\.[^.]+)*(?!\.references)
Message
Column looks like a foreign key but missing .references(). Relations alone don't create DB constraints.
Fix Action
Add .references(() => otherTable.id) for database-level foreign key
Applies To
- **/schema.ts
- */schema/.ts
Select All Columns (No Column Selection)
Id
drizzle-select-star
Severity
info
Type
regex
Pattern
- \\.select\\(\\)\\.from\\(
Message
Selecting all columns. Consider specifying only needed columns for better performance.
Fix Action
Use .select({ col1: table.col1, col2: table.col2 }) to select specific columns
Applies To
- *.ts
- *.tsx
- *.js
Using Beta Version of Drizzle
Id
drizzle-beta-version
Severity
warning
Type
regex
Pattern
- "drizzle-orm":\\s"[^"]beta
- "drizzle-kit":\\s"[^"]beta
Message
Using beta version of Drizzle. May have breaking changes and ecosystem incompatibility.
Fix Action
Consider using stable version unless testing beta features
Applies To
- package.json
Accessing Properties on limit(1) Result
Id
drizzle-limit-one-access
Severity
warning
Type
regex
Pattern
- \\.limit\\(1\\)[^;];\\s\\n[^\\[]*result\\.
- \\.limit\\(1\\)\\s;?\\s$[\\s\\S]{0,50}\\w+\\.(id|name|email|title)
Message
limit(1) returns an array, not single object. Use destructuring [result] or findFirst().
Fix Action
Use const [item] = await query.limit(1) or db.query.table.findFirst()
Applies To
- *.ts
- *.tsx
- *.js
JSONB Column with Default (Push Bug)
Id
drizzle-jsonb-default
Severity
warning
Type
regex
Pattern
- jsonb\\([^)]*\\)\\.default\\(
- json\\([^)]*\\)\\.default\\(
Message
jsonb/json with default may fail with drizzle-kit push. Use generate instead.
Fix Action
Use drizzle-kit generate instead of push, or set default in application code
Applies To
- **/schema.ts
- */schema/.ts
Foreign Key Without onDelete Behavior
Id
drizzle-missing-ondelete
Severity
info
Type
regex
Pattern
- \\.references\\(\\(\\)\\s=>\\s\\w+\\.\\w+\\)(?![^)]*onDelete)
Message
Foreign key without onDelete behavior. Consider adding { onDelete: 'cascade' } or similar.
Fix Action
Add onDelete behavior: .references(() => table.id, { onDelete: 'cascade' })
Applies To
- **/schema.ts
- */schema/.ts
Raw SQL Execution
Id
drizzle-raw-execute
Severity
info
Type
regex
Pattern
- db\\.execute\\(\\s*sql
- \\.execute\\(\\s*`
Message
Using raw SQL execution. Ensure proper parameterization to prevent injection.
Fix Action
Prefer query builder methods. If using raw SQL, use sql placeholder: sql...${param}... without quotes
Applies To
- *.ts
- *.tsx
- *.js
Foreign Key Column Without Index
Id
drizzle-missing-index
Severity
info
Type
regex
Pattern
- \\.references\\([^)]+\\)(?![\\s\\S]{0,200}index\\([^)]*\\))
Message
Foreign key column might benefit from an index for JOIN performance.
Fix Action
Consider adding an index: export const idx = index('idx_name').on(table.column)
Applies To
- **/schema.ts
- */schema/.ts
Transaction Without Await
Id
drizzle-transaction-no-await
Severity
error
Type
regex
Pattern
- db\\.transaction\\([^)]+\\)(?!\\s;?\\s$)(?![\\s\\S]{0,10}await)
Message
Transaction should be awaited. Without await, operations may not complete before continuing.
Fix Action
Add await: await db.transaction(async (tx) => { ... })
Applies To
- *.ts
- *.tsx
- *.js