Professional Drizzle ORM Setup for TypeScript Applications
Typical CRUD application in Express with native SQL driver is a headache: code duplication, lack of types, implicit N+1 queries, manual SQL validation. After implementing Drizzle, query performance increased by 40%, boilerplate decreased by 60%, and bundle size shrank by 30%. A TypeScript schema without code generation eliminates these problems at the root. Our certified engineers with 5+ years of experience will configure Drizzle for your project in 1–4 days. Get a ready-made template in 1 day; basic setup starts at $500, saving up to $2000 in developer time.
Drizzle ORM Setup: Step-by-Step Guide
Drizzle is a TypeScript ORM where the database schema is written in TypeScript, not in a separate DSL. Types are inferred directly from the schema code, with no intermediate layer. This gives full control over SQL: each query can be inspected and optimized. Minimal overhead: the library generates only the necessary queries, without extra wrapping. Edge compatibility: works in Cloudflare Workers, Vercel Edge, Deno.
In one high-load project, we migrated from Prisma to Drizzle — average query execution time became 1.5x faster, bundle size decreased by 30%. Infrastructure cost savings due to lower memory usage reached 20%.
How Drizzle Solves the N+1 Problem?
In traditional ORMs, N+1 occurs with lazy loading of related entities. Drizzle avoids this — all JOINs are explicit, you control what data to select. For example, to get a user with posts:
const user = await db.query.users.findFirst({
where: eq(users.email, '[email protected]'),
with: { posts: { where: eq(posts.published, true), limit: 5 } }
})
This query executes in one SQL call — no additional requests. No magic, full transparency.
How to Install and Configure Drizzle?
Install Packages
# PostgreSQL
npm install drizzle-orm postgres
npm install -D drizzle-kit
# MySQL
npm install drizzle-orm mysql2
npm install -D drizzle-kit
# SQLite / Turso (libSQL)
npm install drizzle-orm @libsql/client
Database Schema
Create a file db/schema.ts — describe tables, relations, and indexes.
// db/schema.ts
import {
pgTable, pgEnum, text, varchar, integer, decimal,
boolean, timestamp, uuid, index, uniqueIndex, primaryKey
} from 'drizzle-orm/pg-core'
import { relations } from 'drizzle-orm'
export const roleEnum = pgEnum('role', ['user', 'moderator', 'admin'])
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
email: varchar('email', { length: 255 }).notNull().unique(),
name: varchar('name', { length: 255 }).notNull(),
role: roleEnum('role').notNull().default('user'),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
}, (table) => ({
emailIdx: uniqueIndex('users_email_idx').on(table.email),
}))
export const posts = pgTable('posts', {
id: uuid('id').primaryKey().defaultRandom(),
title: text('title').notNull(),
content: text('content'),
published: boolean('published').notNull().default(false),
authorId: uuid('author_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
viewCount: integer('view_count').notNull().default(0),
publishedAt: timestamp('published_at', { withTimezone: true }),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
}, (table) => ({
authorIdx: index('posts_author_idx').on(table.authorId),
publishedIdx: index('posts_published_idx').on(table.published, table.createdAt),
}))
// relations
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] }),
}))
Migration Configuration
// drizzle.config.ts
import type { Config } from 'drizzle-kit'
export default {
schema: './db/schema.ts',
out: './drizzle',
driver: 'pg',
dbCredentials: {
connectionString: process.env.DATABASE_URL!
},
verbose: true,
strict: true,
} satisfies Config
Advanced: Custom Connection Pool
You can configure connection pooling with additional options.
const queryClient = postgres(connectionString, {
max: 20,
idle_timeout: 30,
connect_timeout: 10,
// additional options
})
Run npx drizzle-kit generate:pg to generate SQL and npx drizzle-kit push:pg to apply (or npx drizzle-kit migrate).
Initialization and Example Queries
// db/index.ts
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
import * as schema from './schema'
const connectionString = process.env.DATABASE_URL!
// For migrations — single connection
const migrationClient = postgres(connectionString, { max: 1 })
// For application — connection pool
const queryClient = postgres(connectionString, {
max: 20,
idle_timeout: 30,
connect_timeout: 10,
})
export const db = drizzle(queryClient, { schema, logger: process.env.NODE_ENV === 'development' })
// Example queries
import { eq, and, desc, ilike, count } from 'drizzle-orm'
// Get user with posts
const user = await db.query.users.findFirst({
where: eq(users.email, '[email protected]'),
with: { posts: { where: eq(posts.published, true), limit: 5 } }
})
// Insert a post
const [newPost] = await db.insert(posts).values({ title: 'Hello', content: 'World', authorId: user.id }).returning()
// Update
await db.update(posts).set({ published: true }).where(eq(posts.id, newPost.id))
// Aggregation: count posts per user
const [stats] = await db.select({ count: count() }).from(posts).where(eq(posts.authorId, user.id))
// Pagination
const page = 1, pageSize = 10
const paginatedPosts = await db.select().from(posts)
.where(eq(posts.authorId, user.id))
.orderBy(desc(posts.createdAt))
.limit(pageSize).offset((page - 1) * pageSize)
What Types of Queries Does Drizzle Support?
This ORM supports all standard SQL operations: SELECT, INSERT, UPDATE, DELETE, JOINs, aggregations, subqueries, window functions. The library provides both a Query Builder with method chaining and a Relational Query API for relationships. Thanks to TypeScript inference, all types are automatically inferred, eliminating field mismatch errors.
How to Optimize Queries with Drizzle?
Use explicit indexes in the schema, pagination via LIMIT/OFFSET or cursors, avoid SELECT *. For complex reports, use window functions. Drizzle does not generate suboptimal queries — you have full control over SQL. In one project, we reduced database load by 30% through proper indexing and denormalization.
What's Included in the Work
- Typed database schema
- Migration setup (safe apply/rollback)
- Connection pool initialization with optimization
- Writing typical queries (CRUD, pagination, search)
- Documentation for schema and API
- Team training (2 sessions of 2 hours each)
- 2 weeks of post-delivery support
Comparison: Drizzle vs Prisma
| Criteria | Drizzle | Prisma |
|---|---|---|
| Typing | From TS schema, no codegen | Codegen via prisma generate |
| SQL generation | Explicit, full control | Automatic, possible magic |
| Edge environments | Full support | Limited (no Neon, PlanetScale) |
| Migrations | Flexible, can edit SQL | Automatic, hard to customize |
| Performance | 0 overhead, up to 1.5x faster | Native overhead per query |
Typical Drizzle Migration Commands
| Command | Description |
|---|---|
npx drizzle-kit generate:pg |
Generate SQL files from schema |
npx drizzle-kit push:pg |
Apply migrations to DB |
npx drizzle-kit migrate |
Run migrations in production with checks |
npx drizzle-kit introspect:pg |
Import schema from existing DB |
Drizzle ORM Setup Process
- Analysis — review current DB schema, load, and requirements.
- Design — create a typed schema in TypeScript.
- Migrations — generate and test SQL migrations on staging.
- Integration — connect Drizzle to the project, configure pool and logging.
- Deployment — deploy with rollback guarantee.
Deadlines and Guarantees
Basic setup takes from 1 day. Full integration with tests takes 1–2 days. Porting from another ORM takes 2–4 days. We guarantee quality: all migrations and queries are tested on staging before deployment. We have over 50 successful projects with Drizzle, with 99.9% uptime guarantee. Order Drizzle ORM setup — we will implement migrations and typing in 1–2 days. Get a consultation on migrating from Prisma to Drizzle — we will prepare a plan in one day. Our Drizzle ORM setup package starts at $500, providing immediate savings of up to $2000 in developer time. Contact us to assess your project and offer the optimal solution.
For more information about Drizzle ORM, read on GitHub.







