drizzle

Drizzle ORM Schema Style Guide

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "drizzle" with this command: npx skills add lobehub/lobehub/lobehub-lobehub-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

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

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

Foreign Keys

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

Timestamps

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

Indexes

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

Type Inference

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

Example Pattern

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)

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

// ✅ 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

// ✅ Good: explicit select + leftJoin const rows = await this.db .select({ runId: agentEvalRunTopics.runId, score: agentEvalRunTopics.score, testCase: agentEvalTestCases, topic: topics, }) .from(agentEvalRunTopics) .leftJoin(agentEvalTestCases, eq(agentEvalRunTopics.testCaseId, agentEvalTestCases.id)) .leftJoin(topics, eq(agentEvalRunTopics.topicId, topics.id)) .where(eq(agentEvalRunTopics.runId, runId)) .orderBy(asc(agentEvalRunTopics.createdAt));

// ❌ Bad: relational API with with: return this.db.query.agentEvalRunTopics.findMany({ where: eq(agentEvalRunTopics.runId, runId), with: { testCase: true, topic: true }, });

Select with Aggregation

// ✅ Good: select + leftJoin + groupBy const rows = await this.db .select({ id: agentEvalDatasets.id, name: agentEvalDatasets.name, testCaseCount: count(agentEvalTestCases.id).as('testCaseCount'), }) .from(agentEvalDatasets) .leftJoin(agentEvalTestCases, eq(agentEvalDatasets.id, agentEvalTestCases.datasetId)) .groupBy(agentEvalDatasets.id);

One-to-Many (Separate Queries)

When you need a parent record with its children, use two queries instead of relational with: :

// ✅ Good: two simple queries const [dataset] = await this.db .select() .from(agentEvalDatasets) .where(eq(agentEvalDatasets.id, id)) .limit(1);

if (!dataset) return undefined;

const testCases = await this.db .select() .from(agentEvalTestCases) .where(eq(agentEvalTestCases.datasetId, id)) .orderBy(asc(agentEvalTestCases.sortOrder));

return { ...dataset, testCases };

Database Migrations

See the db-migrations skill for the detailed migration guide.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

react

No summary provided by upstream source.

Repository SourceNeeds Review
1.3K-lobehub
General

zustand

No summary provided by upstream source.

Repository SourceNeeds Review
General

project-overview

No summary provided by upstream source.

Repository SourceNeeds Review
General

i18n

No summary provided by upstream source.

Repository SourceNeeds Review