Drizzle
Use Drizzle ORM for type-safe SQL in TypeScript. Use this skill when the project needs to integrate Drizzle for SQL-first database access, schema definition in TypeScript, migrations, relational queries, or connecting to PostgreSQL, MySQL, SQLite, Turso, Neon, PlanetScale, or Vercel Postgres. Covers Drizzle schema, query builder, relational API, migrations, and driver integrations.
You are a database specialist who integrates Drizzle ORM into projects. Drizzle is a TypeScript ORM that stays close to SQL — your schema is TypeScript, your queries look like SQL, and there's zero runtime overhead. If you know SQL, Drizzle feels natural. ## Key Points - Keep schema in a single file or organized by domain with barrel exports - Use `returning()` on insert/update to get the created/updated row - Define relations for the relational query API (`db.query`) - Use Drizzle Kit for migrations — `generate` then `migrate` - Use `drizzle-kit studio` for visual database exploration - Prefer the SQL-like query builder for complex queries with joins - Use the relational API for simpler nested data loading - Not defining indexes in the schema — add them for query performance - Using `push` in production — always use proper migrations - Creating new database connections per request — reuse the client - Not using `returning()` — requires an extra query to get the result - Mixing SQL-like and relational APIs without understanding the difference ## Quick Example ```bash npm install drizzle-orm postgres npm install -D drizzle-kit ```
skilldb get database-services-skills/drizzleFull skill: 292 linesDrizzle ORM Integration
You are a database specialist who integrates Drizzle ORM into projects. Drizzle is a TypeScript ORM that stays close to SQL — your schema is TypeScript, your queries look like SQL, and there's zero runtime overhead. If you know SQL, Drizzle feels natural.
Core Philosophy
SQL-first, not abstraction-first
Drizzle doesn't hide SQL behind a proprietary query language. The query builder maps
directly to SQL constructs — select, from, where, join, groupBy. If you
can write the SQL, you can write the Drizzle query.
Schema in TypeScript
Your database schema is defined as TypeScript code using pgTable, mysqlTable, or
sqliteTable. The schema generates types, migrations, and the query API. No separate
schema language.
Zero runtime overhead
Drizzle compiles queries at the TypeScript level. There's no heavy runtime, no query parsing, no object-relational mapping layer. It generates SQL strings and hands them to your database driver.
Setup
Install (PostgreSQL)
npm install drizzle-orm postgres
npm install -D drizzle-kit
Schema
// src/db/schema.ts
import { pgTable, text, timestamp, boolean, integer, uuid, index } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: uuid('id').defaultRandom().primaryKey(),
email: text('email').notNull().unique(),
name: text('name'),
plan: text('plan').notNull().default('free'),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
}, (table) => [
index('users_email_idx').on(table.email),
]);
export const posts = pgTable('posts', {
id: uuid('id').defaultRandom().primaryKey(),
title: text('title').notNull(),
content: text('content').notNull(),
status: text('status').notNull().default('draft'),
authorId: uuid('author_id').notNull().references(() => users.id),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
}, (table) => [
index('posts_author_idx').on(table.authorId),
index('posts_status_idx').on(table.status, table.createdAt),
]);
export const apiKeys = pgTable('api_keys', {
id: uuid('id').defaultRandom().primaryKey(),
key: text('key').notNull().unique(),
name: text('name').notNull(),
userId: uuid('user_id').notNull().references(() => users.id),
active: boolean('active').notNull().default(true),
usageCount: integer('usage_count').notNull().default(0),
createdAt: timestamp('created_at').defaultNow().notNull(),
}, (table) => [
index('api_keys_user_idx').on(table.userId),
index('api_keys_key_idx').on(table.key),
]);
Initialize
// src/db/index.ts
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
const client = postgres(process.env.DATABASE_URL!);
export const db = drizzle(client, { schema });
Drizzle config
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/db/schema.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: { url: process.env.DATABASE_URL! },
});
Key Techniques
CRUD operations
import { eq, desc, and, like, sql } from 'drizzle-orm';
import { db } from './db';
import { users, posts, apiKeys } from './db/schema';
// Insert
const [user] = await db.insert(users)
.values({ email: 'alice@example.com', name: 'Alice' })
.returning();
// Select
const allPosts = await db.select()
.from(posts)
.where(eq(posts.status, 'published'))
.orderBy(desc(posts.createdAt))
.limit(20);
// Select with join
const postsWithAuthors = await db.select({
id: posts.id,
title: posts.title,
authorName: users.name,
authorEmail: users.email,
})
.from(posts)
.innerJoin(users, eq(posts.authorId, users.id))
.where(eq(posts.status, 'published'));
// Update
await db.update(posts)
.set({ title: 'Updated', updatedAt: new Date() })
.where(eq(posts.id, postId));
// Delete
await db.delete(posts).where(eq(posts.id, postId));
// Upsert
await db.insert(users)
.values({ email: 'alice@example.com', name: 'Alice' })
.onConflictDoUpdate({
target: users.email,
set: { name: 'Alice Updated', updatedAt: new Date() },
});
Relational queries
// Define relations
import { relations } from 'drizzle-orm';
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
apiKeys: many(apiKeys),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.authorId], references: [users.id] }),
}));
// Query with relations (like Prisma include)
const usersWithPosts = await db.query.users.findMany({
with: {
posts: {
where: eq(posts.status, 'published'),
orderBy: desc(posts.createdAt),
limit: 5,
},
},
});
const post = await db.query.posts.findFirst({
where: eq(posts.id, postId),
with: { author: true },
});
Transactions
const result = await db.transaction(async (tx) => {
const [post] = await tx.update(posts)
.set({ status: 'published' })
.where(eq(posts.id, postId))
.returning();
await tx.insert(notifications)
.values({ userId: post.authorId, type: 'published', entityId: post.id });
return post;
});
Aggregations
import { count, avg, sum } from 'drizzle-orm';
const stats = await db.select({
total: count(),
avgViews: avg(posts.viewCount),
})
.from(posts)
.where(eq(posts.authorId, userId));
// Group by
const byStatus = await db.select({
status: posts.status,
count: count(),
})
.from(posts)
.groupBy(posts.status);
Raw SQL
const result = await db.execute(sql`
SELECT * FROM posts
WHERE status = 'published'
AND created_at > ${thirtyDaysAgo}
ORDER BY created_at DESC
`);
Migrations
# Generate migration from schema changes
npx drizzle-kit generate
# Apply migrations
npx drizzle-kit migrate
# Push schema directly (dev only, no migration file)
npx drizzle-kit push
# Open Drizzle Studio (database GUI)
npx drizzle-kit studio
Driver Integrations
// Neon (serverless PostgreSQL)
import { neon } from '@neondatabase/serverless';
import { drizzle } from 'drizzle-orm/neon-http';
const db = drizzle(neon(process.env.DATABASE_URL!));
// Turso (SQLite edge)
import { createClient } from '@libsql/client';
import { drizzle } from 'drizzle-orm/libsql';
const client = createClient({ url: process.env.TURSO_URL!, authToken: process.env.TURSO_TOKEN });
const db = drizzle(client);
// Vercel Postgres
import { sql } from '@vercel/postgres';
import { drizzle } from 'drizzle-orm/vercel-postgres';
const db = drizzle(sql);
// PlanetScale (MySQL)
import { connect } from '@planetscale/database';
import { drizzle } from 'drizzle-orm/planetscale-serverless';
const db = drizzle(connect({ url: process.env.DATABASE_URL! }));
Best Practices
- Keep schema in a single file or organized by domain with barrel exports
- Use
returning()on insert/update to get the created/updated row - Define relations for the relational query API (
db.query) - Use Drizzle Kit for migrations —
generatethenmigrate - Use
drizzle-kit studiofor visual database exploration - Prefer the SQL-like query builder for complex queries with joins
- Use the relational API for simpler nested data loading
Anti-Patterns
- Not defining indexes in the schema — add them for query performance
- Using
pushin production — always use proper migrations - Creating new database connections per request — reuse the client
- Not using
returning()— requires an extra query to get the result - Mixing SQL-like and relational APIs without understanding the difference
- Not closing database connections in scripts and tests
Install this skill directly: skilldb add database-services-skills
Related Skills
Dynamodb
Build with Amazon DynamoDB as a serverless NoSQL database. Use this skill when the project needs to integrate DynamoDB for key-value and document storage, single-table design, global tables, streams, TTL, or serverless-first data access. Covers the AWS SDK v3, single-table design, GSIs, queries, scans, transactions, and DynamoDB Streams.
Fauna
Build with Fauna as a distributed document-relational database. Use this skill when the project needs to integrate Fauna for globally distributed data, ACID transactions, document-relational modeling, FQL queries, or serverless database access. Covers the Fauna v10 client, FQL, schema, indexes, and authentication.
Firebase Firestore
Build with Firebase Firestore as a NoSQL document database. Use this skill when the project needs to integrate Firestore for real-time document storage, offline persistence, security rules, queries, aggregations, or Cloud Functions triggers. Covers the Firestore SDK (v9+), data modeling, queries, security rules, real-time listeners, batched writes, transactions, and Cloud Functions integration.
Mongodb
Build with MongoDB as a document database. Use this skill when the project needs to integrate MongoDB or Mongoose for document storage, flexible schemas, aggregation pipelines, Atlas Search, change streams, or transactions. Covers the MongoDB Node.js driver, Mongoose ODM, schema design, indexing, aggregation, and Atlas features.
Neo4j
Build with Neo4j for graph data and relationship-heavy queries. Use this skill when the project needs a graph database for social networks, recommendation engines, knowledge graphs, fraud detection, or any domain where relationships between entities are central. Covers Cypher query language, the Neo4j JavaScript driver, graph modeling, and common graph patterns.
Neon
Build with Neon as a serverless PostgreSQL database. Use this skill when the project needs to integrate Neon for serverless Postgres, branching, autoscaling, connection pooling, or edge-compatible database access. Covers the Neon serverless driver, branching workflows, connection pooling, and integration with ORMs.