ワンクリックで
drizzle-best-practices
Drizzle ORM done right. Schema design, relations, type-safe queries, migrations, and performance patterns.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Drizzle ORM done right. Schema design, relations, type-safe queries, migrations, and performance patterns.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | drizzle-best-practices |
| description | Drizzle ORM done right. Schema design, relations, type-safe queries, migrations, and performance patterns. |
| metadata | {"tags":"drizzle, database, orm, best-practices"} |
Use this skill when working with Drizzle ORM code. Agents often confuse Drizzle with Prisma and produce wrong schema syntax, incorrect relation patterns, or manual SQL where the query builder should be used.
Wrong (agents do this):
model User {
id Int @id @default(autoincrement())
name String
posts Post[]
}
Correct:
import { pgTable, serial, text } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});
Why: Drizzle uses table-definition functions, not a Prisma-like DSL. Wrong syntax fails at compile time.
Wrong:
// Expecting Prisma-style @relation or Sequelize belongsTo
Correct:
import { relations } from 'drizzle-orm';
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
Why: Drizzle relations are defined separately from tables via relations(). Foreign keys go on
the table; relation metadata goes in relations.
Wrong:
const users = await db.execute(sql`SELECT * FROM users WHERE id = ${id}`);
Correct:
import { eq } from 'drizzle-orm';
const result = await db.select().from(users).where(eq(users.id, id));
Why: Query builder gives type safety, SQL injection protection, and dialect portability. Raw SQL only when the builder cannot express the query.
Wrong:
interface User {
id: number;
name: string;
}
Correct:
type SelectUser = typeof users.$inferSelect;
type InsertUser = typeof users.$inferInsert;
Why: Inferred types stay in sync with schema. Manual types drift and break on schema changes.
Wrong:
for (const id of ids) {
await db.select().from(users).where(eq(users.id, id));
}
Correct:
import { sql } from 'drizzle-orm';
const getById = db
.select()
.from(users)
.where(eq(users.id, sql.placeholder('id')))
.prepare();
for (const id of ids) {
await getById.execute({ id });
}
Why: Prepared statements reduce parse/plan overhead and improve performance for repeated queries.
Wrong:
await db.execute(sql`BEGIN`);
await db.insert(users).values(u);
await db.execute(sql`COMMIT`);
Correct:
await db.transaction(async (tx) => {
await tx.insert(users).values(u);
await tx.insert(posts).values(p);
});
Why: Drizzle transactions handle BEGIN/COMMIT/ROLLBACK and ensure the same connection is used throughout.
Wrong:
// Hand-written 001_create_users.sql
Correct:
drizzle-kit generate
drizzle-kit migrate
Why: Generated migrations stay in sync with schema and support rollback. Manual SQL bypasses Drizzle's migration tracking.
Wrong:
db.select().from(users).where(sql`name = ${name}`);
Correct:
import { eq, like, gt, lt } from 'drizzle-orm';
db.select().from(users).where(eq(users.name, name));
db.select().from(users).where(like(users.name, '%foo%'));
Why: Operators are type-safe and parameterized. Raw template strings risk SQL injection.
Wrong:
const users = await db.select().from(users);
for (const u of users) {
u.posts = await db.select().from(posts).where(eq(posts.authorId, u.id));
}
Correct:
const usersWithPosts = await db.query.users.findMany({
with: { posts: true },
});
Why: Relational API fetches nested data in one call with correct joins and typing.
Wrong:
// Separate migration: CREATE INDEX ...
Correct:
import { index } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
email: text('email').notNull(),
}, (t) => [
index('users_email_idx').on(t.email),
]);
Why: Indexes in schema are versioned and migrated with the rest of the schema.
Wrong:
// Manually maintaining Zod schema that mirrors DB
Correct:
import { createInsertSchema } from 'drizzle-zod';
const insertUserSchema = createInsertSchema(usersTable);
Why: drizzle-zod derives Zod schemas from Drizzle tables, keeping validation in sync.
Wrong:
const db = drizzle(process.env.DATABASE_URL);
Correct:
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
const client = postgres(url, { max: 10, idle_timeout: 20 });
const db = drizzle(client);
Why: Serverless needs bounded connections. Default pooling can exhaust DB connections.
.returning() after insert/update to get affected rowsfindFirst over findMany when expecting a single row.$dynamic() for conditional where/orderBy/limitGenerates a complete, phased, start-to-end TODOS.md build plan for any project — with full detail per task including subtasks, tech stack notes, and acceptance criteria. Use this skill whenever a user wants to plan a project build from scratch, says things like "help me plan my app", "create a todos list for building X", "break down my project into phases", "what should I build first", "project roadmap for X", "generate a build plan", "plan out my SaaS", "what do I need to build", or describes an app idea and wants to know what to build and in what order. Also trigger when the user uploads a project overview, brief, PRD, AGENTS.md, or README and wants a phased build plan from it. Trigger proactively any time a user describes a new app or feature set and hasn't yet planned how to build it — even if they don't say "todos" explicitly.
Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman while keeping full technical accuracy. Supports intensity levels: lite, full (default), ultra, wenyan-lite, wenyan-full, wenyan-ultra. Use when user says "caveman mode", "talk like caveman", "use caveman", "less tokens", "be brief", or invokes /caveman. Also auto-triggers when token efficiency is requested.
Clerk Backend REST API explorer and executor. Browse tags, inspect endpoint schemas, and execute authenticated requests. Use when listing users, managing organizations, or calling any Clerk API endpoint.
Operate the Clerk CLI (`clerk` binary) for authentication, user/org/session management, deploy verification, instance config, env keys, and any Clerk Backend or Platform API call. Use when the user mentions Clerk management tasks, "list clerk users", "create a clerk user", "update organization", "pull clerk config", "clerk env pull", "clerk doctor", "clerk deploy", "clerk deploy status", "clerk api", or any ad-hoc Clerk API request. Prefer the CLI over raw HTTP: it handles auth, key resolution, app/instance targeting, and formatting automatically.
Advanced Next.js patterns - middleware, Server Actions, caching with Clerk.
Add Clerk authentication to any project by following the official quickstart guides.