
Drizzle
Author LobeHub-compatible Drizzle PostgreSQL schemas, joins, and types without breaking strict mode or project naming conventions.
Overview
Drizzle is an agent skill for the Build phase that standardizes Drizzle ORM PostgreSQL schema and query patterns for the LobeHub codebase.
Install
npx skills add https://github.com/lobehub/lobe-chat --skill drizzleWhat is this skill?
- PostgreSQL dialect with `strict: true` and paths under `src/database/schemas/`
- Spread `...timestamps` and helpers from `_helpers.ts` (`timestamptz`, `createdAt`, `updatedAt`)
- `createInsertSchema` plus `$inferSelect` / `$inferInsert` typing patterns
- Query style: `db.select().from().leftJoin()` and guidance on splitting heavy `with:` relations
- Naming: plural snake_case tables, snake_case columns, prefixed text IDs via `idGenerator`
Adoption & trust: 925 installs on skills.sh; 78.4k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need a new table or query in LobeHub but risk wrong timestamp helpers, ID prefixes, or join shapes that fail strict Drizzle review.
Who is it for?
Developers editing LobeChat/LobeHub `src/database/schemas/` or writing `db.select` / `db.query` access layers.
Skip if: Greenfield apps not using LobeHub layout, raw SQL-only stacks, or migration-only tasks without schema edits (use db-migrations instead).
When should I use this skill?
Editing `src/database/schemas/`, defining `pgTable` columns/indexes/junction tables, spreading `...timestamps`, `createInsertSchema` types, or `db.select().from().leftJoin()` queries; for migration files use db-migration
What do I get? / Deliverables
You produce schema and query code that matches LobeHub conventions and know when to split relational loads or hand off to db-migrations.
- Schema module under `src/database/schemas/`
- Typed insert/select patterns
- Query code consistent with project join guidance
Recommended Skills
Journey fit
How it compares
Repo-specific Drizzle style guide—not generic Prisma docs or a database MCP server.
Common Questions / FAQ
Who is drizzle for?
Backend contributors on LobeHub who want the agent to follow existing Drizzle schema and query idioms.
When should I use drizzle?
In build/backend when defining `pgTable` models, indexes, junction tables, or `leftJoin` queries tied to LobeHub paths.
Is drizzle safe to install?
Check this page's Security Audits panel; the skill is documentation-only but edits schemas that affect production data models.
SKILL.md
READMESKILL.md - Drizzle
# 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] })], ); ``` ## Query Style **Always use `db.select()` builder API. Never use `db.query.*` relational API** (`findMany`, `findFirst`, `with:`). The relational API generates complex lateral joins with `json_build_array` that are fragile and hard to debug. ### Select Single Row ```typescript // ✅ Good const [result] = await this.db.select().from(agents).where(eq(agents.id, id)).limit(1); return result; // ❌ Bad: relational API return this.db.query.agents.findFirst({ where: eq(agents.id, id), }); ``` ### Select with JOIN ```typescript // ✅ Good: explicit select + leftJoin const rows = await this.db .select({ runId: agentEvalRunTopics.runId,