drizzle-orm-expert

Expert in Drizzle ORM for TypeScript — schema design, relational queries, migrations, and serverless database integration. Use when building type-safe database layers with Drizzle.

31,392 stars
Complexity: easy

About this skill

The Drizzle ORM Expert skill transforms the AI agent into a production-grade specialist in Drizzle ORM for TypeScript. It's designed to assist developers in building robust, type-safe, and performant database layers. This skill covers a comprehensive range of Drizzle ORM capabilities, including advanced schema design, the intricacies of the relational query API, effective use of Drizzle Kit for migrations, and seamless integration with popular frameworks like Next.js and tRPC. Furthermore, it offers specialized knowledge for connecting Drizzle ORM with serverless databases such as Neon, PlanetScale, Turso, and Supabase, ensuring developers can leverage cutting-edge database solutions.

Best use case

To receive expert advice, best practices, and practical solutions for all aspects of Drizzle ORM development, from initial setup and schema design to complex queries and database migrations, especially within a TypeScript ecosystem.

Expert in Drizzle ORM for TypeScript — schema design, relational queries, migrations, and serverless database integration. Use when building type-safe database layers with Drizzle.

Users can expect to receive clear, accurate, and actionable guidance, code examples, and architectural recommendations related to Drizzle ORM. This includes successfully configured Drizzle ORM setups, well-designed type-safe schemas, optimized relational queries, resolved migration issues, and seamless integrations with specified frameworks and serverless databases, leading to a more efficient and robust database layer.

Practical example

Example input

I'm starting a new Next.js project and want to use Drizzle ORM with a Neon database. Can you walk me through the initial setup, including schema definition for users and posts, and how to perform a basic join query?

Example output

Certainly! To set up Drizzle ORM with Next.js and Neon, follow these steps:

1.  **Project Setup**: Initialize your Next.js project if you haven't already. `npx create-next-app@latest my-drizzle-app`
2.  **Install Drizzle**: Add Drizzle ORM and Drizzle Kit to your project:
    ```bash
    npm install drizzle-orm @neondatabase/serverless
    npm install -D drizzle-kit
    ```
3.  **Neon Database Setup**: Create a new project on Neon.tech and obtain your connection string.
4.  **Environment Variables**: Store your Neon connection string in a `.env` file:
    ```
    DATABASE_URL="your_neon_connection_string"
    ```
5.  **Drizzle Configuration (`drizzle.config.ts`)**: Create a Drizzle Kit configuration file:
    ```typescript
    import { defineConfig } from 'drizzle-kit';

    export default defineConfig({
      schema: './src/db/schema.ts',
      out: './drizzle',
      dialect: 'postgresql',
      dbCredentials: {
        url: process.env.DATABASE_URL as string,
      },
    });
    ```
6.  **Schema Definition (`src/db/schema.ts`)**: Define your `users` and `posts` tables:
    ```typescript
    import { pgTable, serial, text, timestamp, varchar } from 'drizzle-orm/pg-core';
    import { relations } from 'drizzle-orm';

    export const users = pgTable('users', {
      id: serial('id').primaryKey(),
      name: varchar('name', { length: 256 }),
      email: varchar('email', { length: 256 }).unique().notNull(),
      createdAt: timestamp('created_at').defaultNow().notNull(),
    });

    export const posts = pgTable('posts', {
      id: serial('id').primaryKey(),
      title: varchar('title', { length: 256 }).notNull(),
      content: text('content'),
      userId: serial('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
      createdAt: timestamp('created_at').defaultNow().notNull(),
    });

    export const usersRelations = relations(users, ({ many }) => ({
      posts: many(posts),
    }));

    export const postsRelations = relations(posts, ({ one }) => ({
      author: one(users, { fields: [posts.userId], references: [users.id] }),
    }));
    ```
7.  **Run Migrations**: Generate and apply your first migration:
    ```bash
    npx drizzle-kit generate:pg
    npx drizzle-kit push:pg
    ```
8.  **Drizzle Client (`src/db/index.ts`)**: Initialize your Drizzle client:
    ```typescript
    import { drizzle } from 'drizzle-orm/neon-serverless';
    import { Pool } from '@neondatabase/serverless';
    import * as schema from './schema';

    const pool = new Pool({
      connectionString: process.env.DATABASE_URL,
    });

    export const db = drizzle(pool, { schema });
    ```
9.  **Basic Join Query (e.g., in an API route)**:
    To fetch posts along with their authors:
    ```typescript
    // pages/api/posts-with-authors.ts or app/api/posts-with-authors/route.ts
    import type { NextApiRequest, NextApiResponse } from 'next';
    import { db } from '../../db';
    import { posts, users } from '../../db/schema';
    import { eq } from 'drizzle-orm';

    export default async function handler(req: NextApiRequest, res: NextApiResponse) {
      try {
        const result = await db.query.posts.findMany({
          with: {
            author: true,
          },
        });
        res.status(200).json(result);
      } catch (error) {
        console.error('Failed to fetch posts with authors:', error);
        res.status(500).json({ message: 'Internal server error' });
      }
    }
    ```

This provides a robust foundation for your Drizzle ORM application in Next.js with Neon. Let me know if you'd like to dive deeper into specific queries or schema relationships!

When to use this skill

  • When setting up Drizzle ORM in a new or existing TypeScript project.
  • When designing or refining database schemas using Drizzle's TypeScript-first approach.
  • When writing complex relational queries with Drizzle ORM, ensuring type-safety and performance.
  • When implementing or troubleshooting database migrations using Drizzle Kit.

When not to use this skill

  • When the database layer does not utilize Drizzle ORM, or when another ORM (e.g., Prisma, TypeORM) is being used.
  • When the query is unrelated to database development, TypeScript, or Drizzle ORM.
  • When assistance is needed for a database technology not compatible with Drizzle ORM (e.g., NoSQL databases like MongoDB) in a Drizzle context.
  • When the task is purely about frontend development without a database interaction component.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/drizzle-orm-expert/SKILL.md --create-dirs "https://raw.githubusercontent.com/sickn33/antigravity-awesome-skills/main/plugins/antigravity-awesome-skills-claude/skills/drizzle-orm-expert/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/drizzle-orm-expert/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How drizzle-orm-expert Compares

Feature / Agentdrizzle-orm-expertStandard Approach
Platform SupportClaudeLimited / Varies
Context Awareness High Baseline
Installation ComplexityeasyN/A

Frequently Asked Questions

What does this skill do?

Expert in Drizzle ORM for TypeScript — schema design, relational queries, migrations, and serverless database integration. Use when building type-safe database layers with Drizzle.

Which AI agents support this skill?

This skill is designed for Claude.

How difficult is it to install?

The installation complexity is rated as easy. You can find the installation instructions above.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# Drizzle ORM Expert

You are a production-grade Drizzle ORM expert. You help developers build type-safe, performant database layers using Drizzle ORM with TypeScript. You know schema design, the relational query API, Drizzle Kit migrations, and integrations with Next.js, tRPC, and serverless databases (Neon, PlanetScale, Turso, Supabase).

## When to Use This Skill

- Use when the user asks to set up Drizzle ORM in a new or existing project
- Use when designing database schemas with Drizzle's TypeScript-first approach
- Use when writing complex relational queries (joins, subqueries, aggregations)
- Use when setting up or troubleshooting Drizzle Kit migrations
- Use when integrating Drizzle with Next.js App Router, tRPC, or Hono
- Use when optimizing database performance (prepared statements, batching, connection pooling)
- Use when migrating from Prisma, TypeORM, or Knex to Drizzle

## Core Concepts

### Why Drizzle

Drizzle ORM is a TypeScript-first ORM that generates zero runtime overhead. Unlike Prisma (which uses a query engine binary), Drizzle compiles to raw SQL — making it ideal for edge runtimes and serverless. Key advantages:

- **SQL-like API**: If you know SQL, you know Drizzle
- **Zero dependencies**: Tiny bundle, works in Cloudflare Workers, Vercel Edge, Deno
- **Full type inference**: Schema → types → queries are all connected at compile time
- **Relational Query API**: Prisma-like nested includes without N+1 problems

## Schema Design Patterns

### Table Definitions

```typescript
// db/schema.ts
import { pgTable, text, integer, timestamp, boolean, uuid, pgEnum } from "drizzle-orm/pg-core";
import { relations } from "drizzle-orm";

// Enums
export const roleEnum = pgEnum("role", ["admin", "user", "moderator"]);

// Users table
export const users = pgTable("users", {
  id: uuid("id").defaultRandom().primaryKey(),
  email: text("email").notNull().unique(),
  name: text("name").notNull(),
  role: roleEnum("role").default("user").notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull(),
});

// Posts table with foreign key
export const posts = pgTable("posts", {
  id: uuid("id").defaultRandom().primaryKey(),
  title: text("title").notNull(),
  content: text("content"),
  published: boolean("published").default(false).notNull(),
  authorId: uuid("author_id").references(() => users.id, { onDelete: "cascade" }).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
});
```

### Relations

```typescript
// db/relations.ts
export const usersRelations = relations(users, ({ many }) => ({
  posts: many(posts),
}));

export const postsRelations = relations(posts, ({ one }) => ({
  author: one(users, {
    fields: [posts.authorId],
    references: [users.id],
  }),
}));
```

### Type Inference

```typescript
// Infer types directly from your schema — no separate type files needed
import type { InferSelectModel, InferInsertModel } from "drizzle-orm";

export type User = InferSelectModel<typeof users>;
export type NewUser = InferInsertModel<typeof users>;
export type Post = InferSelectModel<typeof posts>;
export type NewPost = InferInsertModel<typeof posts>;
```

## Query Patterns

### Select Queries (SQL-like API)

```typescript
import { eq, and, like, desc, count, sql } from "drizzle-orm";

// Basic select
const allUsers = await db.select().from(users);

// Filtered with conditions
const admins = await db.select().from(users).where(eq(users.role, "admin"));

// Partial select (only specific columns)
const emails = await db.select({ email: users.email }).from(users);

// Join query
const postsWithAuthors = await db
  .select({
    title: posts.title,
    authorName: users.name,
  })
  .from(posts)
  .innerJoin(users, eq(posts.authorId, users.id))
  .where(eq(posts.published, true))
  .orderBy(desc(posts.createdAt))
  .limit(10);

// Aggregation
const postCounts = await db
  .select({
    authorId: posts.authorId,
    postCount: count(posts.id),
  })
  .from(posts)
  .groupBy(posts.authorId);
```

### Relational Queries (Prisma-like API)

```typescript
// Nested includes — Drizzle resolves in a single query
const usersWithPosts = await db.query.users.findMany({
  with: {
    posts: {
      where: eq(posts.published, true),
      orderBy: [desc(posts.createdAt)],
      limit: 5,
    },
  },
});

// Find one with nested data
const user = await db.query.users.findFirst({
  where: eq(users.id, userId),
  with: { posts: true },
});
```

### Insert, Update, Delete

```typescript
// Insert with returning
const [newUser] = await db
  .insert(users)
  .values({ email: "dev@example.com", name: "Dev" })
  .returning();

// Batch insert
await db.insert(posts).values([
  { title: "Post 1", authorId: newUser.id },
  { title: "Post 2", authorId: newUser.id },
]);

// Update
await db.update(users).set({ name: "Updated" }).where(eq(users.id, userId));

// Delete
await db.delete(posts).where(eq(posts.authorId, userId));
```

### Transactions

```typescript
const result = await db.transaction(async (tx) => {
  const [user] = await tx.insert(users).values({ email, name }).returning();
  await tx.insert(posts).values({ title: "Welcome Post", authorId: user.id });
  return user;
});
```

## Migration Workflow (Drizzle Kit)

### Configuration

```typescript
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";

export default defineConfig({
  schema: "./db/schema.ts",
  out: "./drizzle",
  dialect: "postgresql",
  dbCredentials: {
    url: process.env.DATABASE_URL!,
  },
});
```

### Commands

```bash
# Generate migration SQL from schema changes
npx drizzle-kit generate

# Push schema directly to database (development only — skips migration files)
npx drizzle-kit push

# Run pending migrations (production)
npx drizzle-kit migrate

# Open Drizzle Studio (GUI database browser)
npx drizzle-kit studio
```

## Database Client Setup

### PostgreSQL (Neon Serverless)

```typescript
// db/index.ts
import { drizzle } from "drizzle-orm/neon-http";
import { neon } from "@neondatabase/serverless";
import * as schema from "./schema";

const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
```

### SQLite (Turso/LibSQL)

```typescript
import { drizzle } from "drizzle-orm/libsql";
import { createClient } from "@libsql/client";
import * as schema from "./schema";

const client = createClient({
  url: process.env.TURSO_DATABASE_URL!,
  authToken: process.env.TURSO_AUTH_TOKEN,
});
export const db = drizzle(client, { schema });
```

### MySQL (PlanetScale)

```typescript
import { drizzle } from "drizzle-orm/planetscale-serverless";
import { Client } from "@planetscale/database";
import * as schema from "./schema";

const client = new Client({ url: process.env.DATABASE_URL! });
export const db = drizzle(client, { schema });
```

## Performance Optimization

### Prepared Statements

```typescript
// Prepare once, execute many times
const getUserById = db.query.users
  .findFirst({
    where: eq(users.id, sql.placeholder("id")),
  })
  .prepare("get_user_by_id");

// Execute with parameters
const user = await getUserById.execute({ id: "abc-123" });
```

### Batch Operations

```typescript
// Use db.batch() for multiple independent queries in one round-trip
const [allUsers, recentPosts] = await db.batch([
  db.select().from(users),
  db.select().from(posts).orderBy(desc(posts.createdAt)).limit(10),
]);
```

### Indexing in Schema

```typescript
import { index, uniqueIndex } from "drizzle-orm/pg-core";

export const posts = pgTable(
  "posts",
  {
    id: uuid("id").defaultRandom().primaryKey(),
    title: text("title").notNull(),
    authorId: uuid("author_id").references(() => users.id).notNull(),
    createdAt: timestamp("created_at").defaultNow().notNull(),
  },
  (table) => [
    index("posts_author_idx").on(table.authorId),
    index("posts_created_idx").on(table.createdAt),
  ]
);
```

## Next.js Integration

### Server Component Usage

```typescript
// app/users/page.tsx (React Server Component)
import { db } from "@/db";
import { users } from "@/db/schema";

export default async function UsersPage() {
  const allUsers = await db.select().from(users);
  return (
    <ul>
      {allUsers.map((u) => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  );
}
```

### Server Action

```typescript
// app/actions.ts
"use server";
import { db } from "@/db";
import { users } from "@/db/schema";

export async function createUser(formData: FormData) {
  const name = formData.get("name") as string;
  const email = formData.get("email") as string;
  await db.insert(users).values({ name, email });
}
```

## Best Practices

- ✅ **Do:** Keep all schema definitions in a single `db/schema.ts` or split by domain (`db/schema/users.ts`, `db/schema/posts.ts`)
- ✅ **Do:** Use `InferSelectModel` and `InferInsertModel` for type safety instead of manual interfaces
- ✅ **Do:** Use the relational query API (`db.query.*`) for nested data to avoid N+1 problems
- ✅ **Do:** Use prepared statements for frequently executed queries in production
- ✅ **Do:** Use `drizzle-kit generate` + `migrate` in production (never `push`)
- ✅ **Do:** Pass `{ schema }` to `drizzle()` to enable the relational query API
- ❌ **Don't:** Use `drizzle-kit push` in production — it can cause data loss
- ❌ **Don't:** Write raw SQL when the Drizzle query builder supports the operation
- ❌ **Don't:** Forget to define `relations()` if you want to use `db.query.*` with `with`
- ❌ **Don't:** Create a new database connection per request in serverless — use connection pooling

## Troubleshooting

**Problem:** `db.query.tableName` is undefined
**Solution:** Pass all schema objects (including relations) to `drizzle()`: `drizzle(client, { schema })`

**Problem:** Migration conflicts after schema changes
**Solution:** Run `npx drizzle-kit generate` to create a new migration, then `npx drizzle-kit migrate`

**Problem:** Type errors on `.returning()` with MySQL
**Solution:** MySQL does not support `RETURNING`. Use `.execute()` and read `insertId` from the result instead.

Related Skills

mermaid-expert

31392
from sickn33/antigravity-awesome-skills

Create Mermaid diagrams for flowcharts, sequences, ERDs, and architectures. Masters syntax for all diagram types and styling.

Developer ToolsClaude

dwarf-expert

31392
from sickn33/antigravity-awesome-skills

Provides expertise for analyzing DWARF debug files and understanding the DWARF debug format/standard (v3-v5). Triggers when understanding DWARF information, interacting with DWARF files, answering DWARF-related questions, or working with code that parses DWARF data.

Developer ToolsClaude

n8n-expression-syntax

31392
from sickn33/antigravity-awesome-skills

Validate n8n expression syntax and fix common errors. Use when writing n8n expressions, using {{}} syntax, accessing $json/$node variables, troubleshooting expression errors, or working with webhook data in workflows.

Developer ToolsClaude

mcp-builder-ms

31392
from sickn33/antigravity-awesome-skills

Use this skill when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).

Developer ToolsClaude

makepad-deployment

31392
from sickn33/antigravity-awesome-skills

CRITICAL: Use for Makepad packaging and deployment. Triggers on: deploy, package, APK, IPA, 打包, 部署, cargo-packager, cargo-makepad, WASM, Android, iOS, distribution, installer, .deb, .dmg, .nsis, GitHub Actions, CI, action, marketplace

Developer ToolsClaude

macos-menubar-tuist-app

31392
from sickn33/antigravity-awesome-skills

Build, refactor, or review SwiftUI macOS menubar apps that use Tuist.

Developer ToolsClaude

kaizen

31392
from sickn33/antigravity-awesome-skills

Guide for continuous improvement, error proofing, and standardization. Use this skill when the user wants to improve code quality, refactor, or discuss process improvements.

Developer ToolsClaude

issues

31392
from sickn33/antigravity-awesome-skills

Interact with GitHub issues - create, list, and view issues.

Developer ToolsClaude

hugging-face-tool-builder

31392
from sickn33/antigravity-awesome-skills

Your purpose is now is to create reusable command line scripts and utilities for using the Hugging Face API, allowing chaining, piping and intermediate processing where helpful. You can access the API directly, as well as use the hf command line tool.

Developer ToolsClaude

git-pushing

31392
from sickn33/antigravity-awesome-skills

Stage all changes, create a conventional commit, and push to the remote branch. Use when explicitly asks to push changes ("push this", "commit and push"), mentions saving work to remote ("save to github", "push to remote"), or completes a feature and wants to share it.

Developer ToolsClaude

git-hooks-automation

31392
from sickn33/antigravity-awesome-skills

Master Git hooks setup with Husky, lint-staged, pre-commit framework, and commitlint. Automate code quality gates, formatting, linting, and commit message enforcement before code reaches CI.

Developer ToolsClaude

gh-review-requests

31392
from sickn33/antigravity-awesome-skills

Fetch unread GitHub notifications for open PRs where review is requested from a specified team or opened by a team member. Use when asked to "find PRs I need to review", "show my review requests", "what needs my review", "fetch GitHub review requests", or "check team review queue".

Developer ToolsClaude