Claude-skill-registry drizzle-orm
Drizzle ORM for TypeScript - type-safe SQL queries, schema definitions, migrations, and relations. Use when building database layers in Next.js or Node.js applications.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/drizzle-orm" ~/.claude/skills/majiayu000-claude-skill-registry-drizzle-orm-eb4e6a && rm -rf "$T"
manifest:
skills/data/drizzle-orm/SKILL.mdsource content
Drizzle ORM Skill
Type-safe SQL ORM for TypeScript with excellent DX and performance.
Quick Start
Installation
# npm npm install drizzle-orm npm install -D drizzle-kit # pnpm pnpm add drizzle-orm pnpm add -D drizzle-kit # yarn yarn add drizzle-orm yarn add -D drizzle-kit # bun bun add drizzle-orm bun add -D drizzle-kit
Database Drivers
# PostgreSQL (Neon) npm install @neondatabase/serverless # PostgreSQL (node-postgres) npm install pg # PostgreSQL (postgres.js) npm install postgres # MySQL npm install mysql2 # SQLite npm install better-sqlite3
Project Structure
src/ ├── db/ │ ├── index.ts # DB connection │ ├── schema.ts # All schemas │ └── migrations/ # Generated migrations ├── drizzle.config.ts # Drizzle Kit config └── .env
Key Concepts
| Concept | Guide |
|---|---|
| Schema Definition | reference/schema.md |
| Queries | reference/queries.md |
| Relations | reference/relations.md |
| Migrations | reference/migrations.md |
Examples
| Pattern | Guide |
|---|---|
| CRUD Operations | examples/crud.md |
| Complex Queries | examples/complex-queries.md |
| Transactions | examples/transactions.md |
| With Better Auth | examples/better-auth.md |
Templates
| Template | Purpose |
|---|---|
| templates/schema.ts | Schema template |
| templates/db.ts | Database connection |
| templates/drizzle.config.ts | Drizzle Kit config |
Database Connection
Neon (Serverless)
// src/db/index.ts import { neon } from "@neondatabase/serverless"; import { drizzle } from "drizzle-orm/neon-http"; import * as schema from "./schema"; const sql = neon(process.env.DATABASE_URL!); export const db = drizzle(sql, { schema });
Neon (With Connection Pooling)
import { Pool } from "@neondatabase/serverless"; import { drizzle } from "drizzle-orm/neon-serverless"; import * as schema from "./schema"; const pool = new Pool({ connectionString: process.env.DATABASE_URL }); export const db = drizzle(pool, { schema });
Node Postgres
import { Pool } from "pg"; import { drizzle } from "drizzle-orm/node-postgres"; import * as schema from "./schema"; const pool = new Pool({ connectionString: process.env.DATABASE_URL }); export const db = drizzle(pool, { schema });
Schema Definition
// src/db/schema.ts import { pgTable, serial, text, boolean, timestamp, integer, varchar, index, } from "drizzle-orm/pg-core"; import { relations } from "drizzle-orm"; // Users table export const users = pgTable("users", { id: text("id").primaryKey(), email: varchar("email", { length: 255 }).notNull().unique(), name: text("name"), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().notNull(), }); // Tasks table export const tasks = pgTable( "tasks", { id: serial("id").primaryKey(), title: varchar("title", { length: 200 }).notNull(), description: text("description"), completed: boolean("completed").default(false).notNull(), userId: text("user_id") .notNull() .references(() => users.id, { onDelete: "cascade" }), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().notNull(), }, (table) => ({ userIdIdx: index("tasks_user_id_idx").on(table.userId), }) ); // Relations export const usersRelations = relations(users, ({ many }) => ({ tasks: many(tasks), })); export const tasksRelations = relations(tasks, ({ one }) => ({ user: one(users, { fields: [tasks.userId], references: [users.id], }), })); // Types export type User = typeof users.$inferSelect; export type NewUser = typeof users.$inferInsert; export type Task = typeof tasks.$inferSelect; export type NewTask = typeof tasks.$inferInsert;
Drizzle Kit Config
// drizzle.config.ts import { defineConfig } from "drizzle-kit"; export default defineConfig({ schema: "./src/db/schema.ts", out: "./src/db/migrations", dialect: "postgresql", dbCredentials: { url: process.env.DATABASE_URL!, }, });
Migrations
# Generate migration npx drizzle-kit generate # Apply migrations npx drizzle-kit migrate # Push schema directly (development) npx drizzle-kit push # Open Drizzle Studio npx drizzle-kit studio
CRUD Operations
Create
import { db } from "@/db"; import { tasks } from "@/db/schema"; // Insert one const task = await db .insert(tasks) .values({ title: "New task", userId: user.id, }) .returning(); // Insert many const newTasks = await db .insert(tasks) .values([ { title: "Task 1", userId: user.id }, { title: "Task 2", userId: user.id }, ]) .returning();
Read
import { eq, and, desc } from "drizzle-orm"; // Get all tasks for user const userTasks = await db .select() .from(tasks) .where(eq(tasks.userId, user.id)) .orderBy(desc(tasks.createdAt)); // Get single task const task = await db .select() .from(tasks) .where(and(eq(tasks.id, taskId), eq(tasks.userId, user.id))) .limit(1); // With relations const tasksWithUser = await db.query.tasks.findMany({ where: eq(tasks.userId, user.id), with: { user: true, }, });
Update
const updated = await db .update(tasks) .set({ completed: true, updatedAt: new Date(), }) .where(and(eq(tasks.id, taskId), eq(tasks.userId, user.id))) .returning();
Delete
await db .delete(tasks) .where(and(eq(tasks.id, taskId), eq(tasks.userId, user.id)));
Query Helpers
import { eq, ne, gt, lt, gte, lte, like, ilike, and, or, not, isNull, isNotNull, inArray, between, sql } from "drizzle-orm"; // Comparison eq(tasks.id, 1) // = ne(tasks.id, 1) // != gt(tasks.id, 1) // > gte(tasks.id, 1) // >= lt(tasks.id, 1) // < lte(tasks.id, 1) // <= // String like(tasks.title, "%test%") // LIKE ilike(tasks.title, "%test%") // ILIKE (case-insensitive) // Logical and(eq(tasks.userId, id), eq(tasks.completed, false)) or(eq(tasks.status, "pending"), eq(tasks.status, "active")) not(eq(tasks.completed, true)) // Null checks isNull(tasks.description) isNotNull(tasks.description) // Arrays inArray(tasks.status, ["pending", "active"]) // Range between(tasks.createdAt, startDate, endDate) // Raw SQL sql`${tasks.title} || ' - ' || ${tasks.description}`
Transactions
await db.transaction(async (tx) => { const [task] = await tx .insert(tasks) .values({ title: "New task", userId: user.id }) .returning(); await tx.insert(taskHistory).values({ taskId: task.id, action: "created", }); });
Server Actions (Next.js)
// app/actions/tasks.ts "use server"; import { db } from "@/db"; import { tasks } from "@/db/schema"; import { eq, and } from "drizzle-orm"; import { revalidatePath } from "next/cache"; import { auth } from "@/lib/auth"; export async function createTask(formData: FormData) { const session = await auth(); if (!session?.user) throw new Error("Unauthorized"); const title = formData.get("title") as string; await db.insert(tasks).values({ title, userId: session.user.id, }); revalidatePath("/tasks"); } export async function toggleTask(taskId: number) { const session = await auth(); if (!session?.user) throw new Error("Unauthorized"); const [task] = await db .select() .from(tasks) .where(and(eq(tasks.id, taskId), eq(tasks.userId, session.user.id))); if (!task) throw new Error("Task not found"); await db .update(tasks) .set({ completed: !task.completed }) .where(eq(tasks.id, taskId)); revalidatePath("/tasks"); }