Claude Code Plugins

Community-maintained marketplace

Feedback

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 Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name drizzle-orm
description 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.

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");
}