While Prisma dominated the last few years, 2025 is the year of Drizzle ORM. If you’re looking for a lightweight, "serverless-first" ORM that feels like writing raw SQL but provides world-class TypeScript autocompletion, this guide is for you.
1. Project Initialization
We’ll set up a modern Node.js environment using TypeScript. Drizzle is designed to be extremely thin, making it perfect for Edge functions and standard Node backends alike.
mkdir drizzle-postgres-2025
cd drizzle-postgres-2025
npm init -y
npm install typescript tsx @types/node --save-dev
npx tsc --init
2. Install Drizzle and PostgreSQL Driver
Drizzle doesn't use a heavy Rust engine. It’s just TypeScript. We’ll use the postgres package (the fastest JS client) and drizzle-kit for migrations.
npm install drizzle-orm postgres
npm install -D drizzle-kit
3. Configure Your Connection
Create a .env file to hold your PostgreSQL connection string. Whether you’re using Neon, Supabase, or a local Docker instance, the format remains:
DATABASE_URL="postgresql://user:password@localhost:5432/my_db"
4. Define Your Schema in TypeScript
Unlike Prisma, which uses a custom .prisma language, Drizzle uses pure TypeScript. This means you can use functions and variables inside your schema definitions.
Create src/db/schema.ts:
import { pgTable, serial, text, boolean, integer, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').unique().notNull(),
createdAt: timestamp('created_at').defaultNow(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content'),
published: boolean('published').default(false),
authorId: integer('author_id').references(() => users.id),
});
5. Generate and Push Migrations
Drizzle Kit reads your TypeScript schema and generates the SQL for you.
Create a drizzle.config.ts in your root:
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/db/schema.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
Now, run the migration to sync your DB:
npx drizzle-kit push
6. Instantiate the Client
Create src/db/index.ts. Drizzle is lightweight enough that you don't usually need a complex singleton pattern, but it's good practice for hot-reloading:
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
const queryClient = postgres(process.env.DATABASE_URL!);
export const db = drizzle(queryClient, { schema });
7. Performing Type-Safe CRUD
Drizzle gives you two ways to query: SQL-like (for those who love SQL) and Relational Queries (for those who love the Prisma API).
The "Prisma-like" way (Relational):
import { db } from './db';
async function main() {
// Create a user with a post
const newUser = await db.insert(users).values({
name: 'Dev Reader',
email: 'hello@dev.to',
}).returning();
// Fetch users with their posts (The "Prisma" feel)
const allUsers = await db.query.users.findMany({
with: {
posts: true,
},
});
console.log(allUsers);
}
main();
🚀 What Makes Drizzle the 2025 Choice?
-
Zero Code-Gen: You don't need to run
npx generateevery time you change a field. Your TypeScript types are always in sync with your schema automatically. - The "Edge" Advantage: Because Drizzle has no heavy binary engine, it starts up in milliseconds on Vercel Functions or Cloudflare Workers.
-
Drizzle Studio: Just like Prisma Studio, run
npx drizzle-kit studioto get a beautiful browser-based UI to explore your PostgreSQL data. - SQL Power: If you need to write a complex window function or a specific PostgreSQL extension query, Drizzle stays out of your way and lets you write SQL while still providing types for the result.
Pro-Tip: If you are migrating from Prisma, check out the drizzle-kit introspect command. It can look at your existing database and automatically generate the Drizzle TypeScript schema for you!
Top comments (0)