home / skills / lobehub / lobe-chat / drizzle

drizzle skill

/.agents/skills/drizzle

This skill helps you design and migrate PostgreSQL schemas with Drizzle ORM, ensuring type-safe, scalable database models and reliable migrations.

npx playbooks add skill lobehub/lobe-chat --skill drizzle

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
3.3 KB
---
name: drizzle
description: Drizzle ORM schema and database guide. Use when working with database schemas (src/database/schemas/*), defining tables, creating migrations, or database model code. Triggers on Drizzle schema definition, database migrations, or ORM usage questions.
---

# Drizzle ORM Schema Style Guide

## Configuration

- Config: `drizzle.config.ts`
- Schemas: `src/database/schemas/`
- Migrations: `src/database/migrations/`
- Dialect: `postgresql` with `strict: true`

## Helper Functions

Location: `src/database/schemas/_helpers.ts`

- `timestamptz(name)`: Timestamp with timezone
- `createdAt()`, `updatedAt()`, `accessedAt()`: Standard timestamp columns
- `timestamps`: Object with all three for easy spread

## Naming Conventions

- **Tables**: Plural snake_case (`users`, `session_groups`)
- **Columns**: snake_case (`user_id`, `created_at`)

## Column Definitions

### Primary Keys

```typescript
id: text('id')
  .primaryKey()
  .$defaultFn(() => idGenerator('agents'))
  .notNull(),
```

ID prefixes make entity types distinguishable. For internal tables, use `uuid`.

### Foreign Keys

```typescript
userId: text('user_id')
  .references(() => users.id, { onDelete: 'cascade' })
  .notNull(),
```

### Timestamps

```typescript
...timestamps,  // Spread from _helpers.ts
```

### Indexes

```typescript
// Return array (object style deprecated)
(t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
```

## Type Inference

```typescript
export const insertAgentSchema = createInsertSchema(agents);
export type NewAgent = typeof agents.$inferInsert;
export type AgentItem = typeof agents.$inferSelect;
```

## Example Pattern

```typescript
export const agents = pgTable(
  'agents',
  {
    id: text('id').primaryKey().$defaultFn(() => idGenerator('agents')).notNull(),
    slug: varchar('slug', { length: 100 }).$defaultFn(() => randomSlug(4)).unique(),
    userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
    clientId: text('client_id'),
    chatConfig: jsonb('chat_config').$type<LobeAgentChatConfig>(),
    ...timestamps,
  },
  (t) => [uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId)],
);
```

## Common Patterns

### Junction Tables (Many-to-Many)

```typescript
export const agentsKnowledgeBases = pgTable(
  'agents_knowledge_bases',
  {
    agentId: text('agent_id').references(() => agents.id, { onDelete: 'cascade' }).notNull(),
    knowledgeBaseId: text('knowledge_base_id').references(() => knowledgeBases.id, { onDelete: 'cascade' }).notNull(),
    userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
    enabled: boolean('enabled').default(true),
    ...timestamps,
  },
  (t) => [primaryKey({ columns: [t.agentId, t.knowledgeBaseId] })],
);
```

## Database Migrations

See `references/db-migrations.md` for detailed migration guide.

```bash
# Generate migrations
bun run db:generate

# After modifying SQL (e.g., adding IF NOT EXISTS)
bun run db:generate:client
```

### Migration Best Practices

```sql
-- ✅ Idempotent operations
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "avatar" text;
DROP TABLE IF EXISTS "old_table";
CREATE INDEX IF NOT EXISTS "users_email_idx" ON "users" ("email");

-- ❌ Non-idempotent
ALTER TABLE "users" ADD COLUMN "avatar" text;
```

Rename migration files meaningfully: `0046_meaningless.sql` → `0046_user_add_avatar.sql`

Overview

This skill is a practical Drizzle ORM schema and database guide for TypeScript projects. It codifies naming, column patterns, timestamp helpers, index usage, and migration best practices to keep schemas consistent and migrations safe. Use it when defining tables, creating migrations, or inferring types from Drizzle tables.

How this skill works

It inspects Drizzle schema files in src/database/schemas/* and provides guidance on table definitions, helper utilities, and common patterns like junction tables. It explains how to define primary keys, foreign keys, timestamps, indexes, and how to generate and harden migrations for PostgreSQL with strict dialect settings. It also shows how to infer TypeScript types from Drizzle tables for safe CRUD operations.

When to use it

  • Creating or updating table definitions under src/database/schemas/
  • Designing many-to-many relationships (junction tables)
  • Adding timestamps, indexes, or foreign keys with cascades
  • Generating and reviewing SQL migrations for Postgres
  • Inferring TypeScript types from Drizzle tables for insert/select operations

Best practices

  • Use plural snake_case for table names and snake_case for columns
  • Prefix IDs to indicate entity type; use uuid for internal tables
  • Spread standard timestamps from helper functions (createdAt, updatedAt, accessedAt)
  • Return arrays for indexes (object style is deprecated); prefer uniqueIndex named explicitly
  • Make migrations idempotent (IF NOT EXISTS / DROP IF EXISTS) and name files meaningfully

Example use cases

  • Define agents table with id text primary key, user reference, config jsonb, and timestamps
  • Create a agents_knowledge_bases junction table with composite primary key and enabled flag
  • Add a new nullable column to users using an idempotent ALTER TABLE ... IF NOT EXISTS migration
  • Generate TypeScript types with createInsertSchema and use $inferInsert / $inferSelect in services
  • Add a unique index over client_id and user_id using the (t) => [...] index pattern

FAQ

How should I name tables and columns?

Use plural snake_case for tables (e.g., users) and snake_case for columns (e.g., user_id, created_at).

How do I keep migrations safe across environments?

Write idempotent SQL (IF NOT EXISTS, DROP IF EXISTS, CREATE INDEX IF NOT EXISTS) and give migration files meaningful names.