AgentSkillsCN

postgres-drizzle

PostgreSQL 与 Drizzle ORM 的最佳实践。触发条件:PostgreSQL、Postgres、Drizzle、数据库、 模式、表、列、索引、查询、迁移、ORM、关系、联结、事务、SQL、 drizzle-kit、连接池、N+1 问题、JSONB、RLS。 在编写数据库模式、查询、迁移,或任何与数据库相关的代码时使用。在创建 API、后端或数据模型时,主动应用这些最佳实践。

SKILL.md
--- frontmatter
name: postgres-drizzle
description: |
  PostgreSQL and Drizzle ORM best practices. Triggers on: PostgreSQL, Postgres, Drizzle, database,
  schema, tables, columns, indexes, queries, migrations, ORM, relations, joins, transactions, SQL,
  drizzle-kit, connection pooling, N+1, JSONB, RLS.

  Use when: writing database schemas, queries, migrations, or any database-related code.
  Proactively apply when creating APIs, backends, or data models.

PostgreSQL + Drizzle ORM

Type-safe database applications with PostgreSQL 18 and Drizzle ORM.

Essential Commands

bash
npx drizzle-kit generate   # Generate migration from schema changes
npx drizzle-kit migrate    # Apply pending migrations
npx drizzle-kit push       # Push schema directly (dev only!)
npx drizzle-kit studio     # Open database browser

Quick Decision Trees

"How do I model this relationship?"

code
Relationship type?
├─ One-to-many (user has posts)     → FK on "many" side + relations()
├─ Many-to-many (posts have tags)   → Junction table + relations()
├─ One-to-one (user has profile)    → FK with unique constraint
└─ Self-referential (comments)      → FK to same table

"Why is my query slow?"

code
Slow query?
├─ Missing index on WHERE/JOIN columns  → Add index
├─ N+1 queries in loop                  → Use relational queries API
├─ Full table scan                      → EXPLAIN ANALYZE, add index
├─ Large result set                     → Add pagination (limit/offset)
└─ Connection overhead                  → Enable connection pooling

"Which drizzle-kit command?"

code
What do I need?
├─ Schema changed, need SQL migration   → drizzle-kit generate
├─ Apply migrations to database         → drizzle-kit migrate
├─ Quick dev iteration (no migration)   → drizzle-kit push
└─ Browse/edit data visually            → drizzle-kit studio

Directory Structure

code
src/db/
├── schema/
│   ├── index.ts          # Re-export all tables
│   ├── users.ts          # Table + relations
│   └── posts.ts          # Table + relations
├── db.ts                 # Connection with pooling
└── migrate.ts            # Migration runner
drizzle/
└── migrations/           # Generated SQL files
drizzle.config.ts         # drizzle-kit config

Schema Patterns

Basic Table with Timestamps

typescript
export const users = pgTable('users', {
  id: uuid('id').primaryKey().defaultRandom(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  createdAt: timestamp('created_at').defaultNow().notNull(),
  updatedAt: timestamp('updated_at').defaultNow().notNull(),
})

Foreign Key with Index

typescript
export const posts = pgTable(
  'posts',
  {
    id: uuid('id').primaryKey().defaultRandom(),
    userId: uuid('user_id')
      .notNull()
      .references(() => users.id),
    title: varchar('title', { length: 255 }).notNull(),
  },
  (table) => [
    index('posts_user_id_idx').on(table.userId), // ALWAYS index FKs
  ],
)

Relations

typescript
export const usersRelations = relations(users, ({ many }) => ({
  posts: many(posts),
}))

export const postsRelations = relations(posts, ({ one }) => ({
  author: one(users, { fields: [posts.userId], references: [users.id] }),
}))

Query Patterns

Relational Query (Avoid N+1)

typescript
// ✓ Single query with nested data
const usersWithPosts = await db.query.users.findMany({
  with: { posts: true },
})

Filtered Query

typescript
const activeUsers = await db.select().from(users).where(eq(users.status, 'active'))

Transaction

typescript
await db.transaction(async (tx) => {
  const [user] = await tx.insert(users).values({ email }).returning()
  await tx.insert(profiles).values({ userId: user.id })
})

Performance Checklist

PriorityCheckImpact
CRITICALIndex all foreign keysPrevents full table scans on JOINs
CRITICALUse relational queries for nested dataAvoids N+1
HIGHConnection pooling in productionReduces connection overhead
HIGHEXPLAIN ANALYZE slow queriesIdentifies missing indexes
MEDIUMPartial indexes for filtered subsetsSmaller, faster indexes
MEDIUMUUIDv7 for PKs (PG18+)Better index locality

Anti-Patterns (CRITICAL)

Anti-PatternProblemFix
No FK indexSlow JOINs, full scansAdd index on every FK column
N+1 in loopsQuery per rowUse with: relational queries
No poolingConnection per requestUse @neondatabase/serverless or similar
push in prodData loss riskAlways use generate + migrate
Storing JSON as textNo validation, bad queriesUse jsonb() column type

Reference Documentation

FilePurpose
references/SCHEMA.mdColumn types, constraints
references/QUERIES.mdOperators, joins, aggregations
references/RELATIONS.mdOne-to-many, many-to-many
references/MIGRATIONS.mddrizzle-kit workflows
references/POSTGRES.mdPG18 features, RLS, partitioning
references/PERFORMANCE.mdIndexing, optimization
references/CHEATSHEET.mdQuick reference

Resources

Drizzle ORM

PostgreSQL