Drizzle ORM — TypeScript ORM siêu nhẹ đang thay đổi cách viết SQL
Posted on: 4/27/2026 7:15:16 AM
Table of contents
- 1. Drizzle ORM là gì?
- 2. Kiến trúc: Tại sao Drizzle nhỏ đến vậy?
- 3. Định nghĩa Schema — Code-first bằng TypeScript
- 4. Query Builder — SQL mà bạn đã biết
- 5. Relational Queries — Khi cần API style
- 6. So sánh Hiệu năng: Drizzle vs Prisma 7
- 7. Migration — Hai chế độ linh hoạt
- 8. Database Support
- 9. Tích hợp với Vue.js và Nuxt
- 10. Khi nào chọn Drizzle, khi nào chọn Prisma?
- 11. Quick Start — 5 phút với Drizzle
- 12. Kết luận
1. Drizzle ORM là gì?
Drizzle ORM là một TypeScript ORM theo hướng SQL-first — nghĩa là bạn viết schema và query gần như giống SQL thuần, nhưng được bọc trong TypeScript với full type safety. Không có query engine riêng, không có binary Rust, không có bước code generation — tất cả đều là TypeScript thuần từ đầu đến cuối.
Trong khi Prisma chọn cách trừu tượng hoá SQL phía sau một DSL riêng (.prisma schema), Drizzle đi ngược lại: giữ nguyên SQL mental model nhưng thêm type safety và developer experience hiện đại. Triết lý là: "Nếu bạn biết SQL, bạn đã biết Drizzle."
Tại sao Drizzle nổi lên năm 2026?
Sự bùng nổ của edge computing (Cloudflare Workers, Vercel Edge Functions) đặt ra yêu cầu khắt khe về bundle size và cold start. Prisma 5.x với Rust query engine nặng ~13MB gần như không thể deploy lên edge. Drizzle với ~12KB runtime trở thành lựa chọn tự nhiên cho edge-first applications.
2. Kiến trúc: Tại sao Drizzle nhỏ đến vậy?
graph TB
subgraph Prisma["Prisma Architecture"]
PA["Schema DSL
(.prisma file)"] -->|"prisma generate"| PB["Generated Client
+ Types"]
PB --> PC["TypeScript/WASM
Query Engine ~1.6MB"]
PC --> PD["Database Driver"]
end
subgraph Drizzle["Drizzle Architecture"]
DA["TypeScript Schema
(tables, columns)"] -->|"Direct import"| DB["Query Builder
~12KB total"]
DB --> DC["Database Driver"]
end
PD --> E["PostgreSQL / MySQL
SQLite / SQL Server"]
DC --> E
style PA fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style PB fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style PC fill:#ff9800,stroke:#fff,color:#fff
style PD fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style DA fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style DB fill:#e94560,stroke:#fff,color:#fff
style DC fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style E fill:#2c3e50,stroke:#fff,color:#fff
Drizzle bỏ hoàn toàn lớp trung gian. Schema được định nghĩa bằng TypeScript thuần — pgTable(), mysqlTable(), sqliteTable() — trình query builder đọc schema này và sinh ra SQL string tương ứng. Không có bước generate, không có file trung gian, types luôn đồng bộ với source code.
3. Định nghĩa Schema — Code-first bằng TypeScript
Một trong những điểm mạnh nhất của Drizzle: schema là TypeScript code thực sự, không phải DSL riêng.
// schema.ts — Drizzle schema definition
import { pgTable, serial, text, integer, timestamp, boolean } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
email: text('email').notNull().unique(),
name: text('name').notNull(),
avatarUrl: text('avatar_url'),
isActive: boolean('is_active').default(true),
createdAt: timestamp('created_at').defaultNow(),
});
export const posts = pgTable('posts', {
id: serial('id').primaryKey(),
title: text('title').notNull(),
content: text('content'),
authorId: integer('author_id')
.notNull()
.references(() => users.id),
publishedAt: timestamp('published_at'),
viewCount: integer('view_count').default(0),
});
// Types được infer tự động — không cần generate
type User = typeof users.$inferSelect;
type NewUser = typeof users.$inferInsert;
So sánh với Prisma schema tương đương:
// schema.prisma — Prisma DSL
model User {
id Int @id @default(autoincrement())
email String @unique
name String
avatarUrl String? @map("avatar_url")
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at")
posts Post[]
@@map("users")
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
authorId Int @map("author_id")
author User @relation(fields: [authorId], references: [id])
publishedAt DateTime? @map("published_at")
viewCount Int @default(0) @map("view_count")
@@map("posts")
}
// Sau đó phải chạy: npx prisma generate
Ưu điểm code-first
Vì schema là TypeScript, bạn có thể dùng mọi tính năng của ngôn ngữ: chia schema thành nhiều file, import/export, conditional logic, shared utilities. Không bị giới hạn bởi syntax của một DSL riêng.
4. Query Builder — SQL mà bạn đã biết
Drizzle query builder map gần như 1:1 với SQL syntax. Nếu biết viết SQL, bạn biết viết Drizzle.
import { db } from './db';
import { users, posts } from './schema';
import { eq, gt, desc, sql, and, like } from 'drizzle-orm';
// SELECT cơ bản
const allUsers = await db.select().from(users);
// WHERE + ORDER BY
const activeUsers = await db
.select()
.from(users)
.where(eq(users.isActive, true))
.orderBy(desc(users.createdAt));
// JOIN
const usersWithPosts = await db
.select({
userName: users.name,
postTitle: posts.title,
views: posts.viewCount,
})
.from(users)
.innerJoin(posts, eq(users.id, posts.authorId))
.where(gt(posts.viewCount, 100));
// INSERT returning
const [newUser] = await db
.insert(users)
.values({ email: 'dev@example.com', name: 'Dev' })
.returning();
// UPDATE
await db
.update(posts)
.set({ viewCount: sql`${posts.viewCount} + 1` })
.where(eq(posts.id, 42));
// Subquery
const topAuthors = await db
.select({
name: users.name,
totalViews: sql<number>`sum(${posts.viewCount})`.as('total_views'),
})
.from(users)
.innerJoin(posts, eq(users.id, posts.authorId))
.groupBy(users.name)
.having(gt(sql`sum(${posts.viewCount})`, 1000))
.orderBy(desc(sql`total_views`));
5. Relational Queries — Khi cần API style
Bên cạnh SQL-style query builder, Drizzle cũng cung cấp Relational Query API cho những ai thích Prisma-style nested queries:
import { relations } from 'drizzle-orm';
// Định nghĩa relations
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
// Query với nested relations — giống Prisma
const result = await db.query.users.findMany({
with: {
posts: {
where: gt(posts.viewCount, 50),
orderBy: desc(posts.publishedAt),
limit: 5,
},
},
where: eq(users.isActive, true),
});
6. So sánh Hiệu năng: Drizzle vs Prisma 7
| Tiêu chí | Drizzle 0.45.x | Prisma 7.x | Prisma 5.x (cũ) |
|---|---|---|---|
| Bundle size | ~12KB | ~1.6MB (600KB gzip) | ~13MB |
| Cold start (Lambda) | 50-100ms | 80-150ms | 600-1800ms |
| Simple query overhead | 0.5-1ms | 1-2ms | 3-5ms |
| Complex join overhead | 1-3ms | 2-5ms | 5-10ms |
| Code generation | Không cần | Cần prisma generate | Cần |
| Dependencies | 0 | Nhiều | Nhiều + Rust binary |
| Tree-shakeable | Có | Một phần | Không |
Benchmark trong bối cảnh thực tế
Chênh lệch query overhead (0.5ms vs 2ms) hiếm khi là bottleneck. Database round-trip latency thường là 5-50ms — ORM overhead chỉ chiếm 2-10% tổng thời gian. Drizzle thực sự vượt trội ở bundle size và cold start — hai yếu tố sống còn trên edge và serverless.
7. Migration — Hai chế độ linh hoạt
Drizzle Kit cung cấp hai workflow migration:
7.1 Generate + Migrate (Production)
# Sinh file migration SQL từ schema changes
npx drizzle-kit generate --name add_user_avatar
# Xem SQL sẽ được chạy
cat drizzle/0001_add_user_avatar.sql
# Áp dụng migration
npx drizzle-kit migrate
7.2 Push (Prototyping)
# Đẩy schema trực tiếp lên DB, không tạo migration file
npx drizzle-kit push
# Phù hợp cho development nhanh, KHÔNG dùng cho production
7.3 Drizzle Studio — GUI quản lý data
# Mở web UI để browse và edit data
npx drizzle-kit studio
Drizzle Studio chạy trên browser, hỗ trợ xem schema, query data, edit record trực tiếp — tương tự Prisma Studio nhưng không cần cài đặt riêng.
8. Database Support
| Database | Driver | Edge-compatible |
|---|---|---|
| PostgreSQL | postgres, pg, @neondatabase/serverless | Neon, Supabase |
| MySQL | mysql2, @planetscale/database | PlanetScale |
| SQLite | better-sqlite3, @libsql/client | Turso, Cloudflare D1 |
| SQL Server | Community driver | Hạn chế |
| Gel (mới) | Gel dialect riêng | Đang phát triển |
Edge Database tối ưu
Kết hợp Drizzle + Turso (libSQL trên edge) hoặc Drizzle + Cloudflare D1 cho database layer hoàn toàn trên edge — zero latency đến database, cold start gần như tức thời. Đây là stack mà nhiều startup đang chọn năm 2026.
9. Tích hợp với Vue.js và Nuxt
Drizzle hoạt động hoàn hảo trong Nuxt server routes và Vue full-stack apps:
// server/utils/db.ts — Nuxt 4 server utility
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from '~/server/db/schema';
const client = postgres(process.env.DATABASE_URL!);
export const db = drizzle(client, { schema });
// server/api/posts.get.ts — API route
export default defineEventHandler(async () => {
return db.query.posts.findMany({
with: { author: true },
where: eq(schema.posts.publishedAt, sql`IS NOT NULL`),
orderBy: desc(schema.posts.publishedAt),
limit: 20,
});
});
10. Khi nào chọn Drizzle, khi nào chọn Prisma?
graph TD
A["Chọn TypeScript ORM"] --> B{"Deploy lên edge?
Bundle size quan trọng?"}
B -->|"Có"| C["✅ Drizzle"]
B -->|"Không"| D{"Team quen SQL?"}
D -->|"Có"| E["✅ Drizzle"]
D -->|"Không, muốn
abstraction cao"| F["✅ Prisma"]
A --> G{"Cần ecosystem
mạnh (Studio,
Accelerate)?"}
G -->|"Có"| H["✅ Prisma"]
G -->|"Không, cần
nhẹ và nhanh"| I["✅ Drizzle"]
style A fill:#2c3e50,stroke:#fff,color:#fff
style C fill:#e94560,stroke:#fff,color:#fff
style E fill:#e94560,stroke:#fff,color:#fff
style F fill:#4CAF50,stroke:#fff,color:#fff
style H fill:#4CAF50,stroke:#fff,color:#fff
style I fill:#e94560,stroke:#fff,color:#fff
style B fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style D fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
style G fill:#f8f9fa,stroke:#e0e0e0,color:#2c3e50
| ✅ Chọn Drizzle | ✅ Chọn Prisma |
|---|---|
| Deploy edge / serverless (bundle nhỏ) | Team mới với SQL, cần abstraction |
| Team thành thạo SQL | Dự án lớn cần ecosystem mạnh |
| Không muốn bước code generation | Thích schema DSL rõ ràng |
| Cần tree-shaking tối đa | Cần Prisma Accelerate (connection pooling) |
| Đa database backend | Team quen Prisma workflow |
11. Quick Start — 5 phút với Drizzle
# Cài đặt
npm install drizzle-orm postgres
npm install -D drizzle-kit
# Tạo config
# drizzle.config.ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/schema.ts',
out: './drizzle',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});
# Tạo schema, push lên DB, bắt đầu code
npx drizzle-kit push
npx drizzle-kit studio # Mở GUI
12. Kết luận
Drizzle ORM đại diện cho một thế hệ công cụ mới: nhẹ, type-safe, SQL-first, và tối ưu cho thời đại edge computing. Với bundle size chỉ 12KB, zero dependencies, và cold start nhanh gấp đôi Prisma, Drizzle là lựa chọn tự nhiên cho các dự án cần deploy trên Cloudflare Workers, Vercel Edge, hay bất kỳ môi trường serverless nào.
Prisma vẫn là ORM mạnh mẽ với ecosystem đồ sộ — đặc biệt sau khi Prisma 7 loại bỏ Rust engine. Nhưng nếu bạn muốn một ORM "nghĩ cùng bạn" thay vì "nghĩ thay bạn", Drizzle xứng đáng được thử.
Bắt đầu từ đâu?
Nếu dùng Nuxt/Vue: tham khảo Drizzle Quick Start + setup server/utils/db.ts. Nếu dùng Node.js thuần: npm install drizzle-orm postgres và bắt đầu định nghĩa schema. Toàn bộ quá trình từ install đến query đầu tiên mất khoảng 5 phút.
Nguồn tham khảo:
ClickHouse 26.x — Columnar Database cho Real-Time Analytics tỷ dòng dữ liệu mỗi giây
Vite 8 + Rolldown: Bundler Rust Thống Nhất Thay Đổi Cuộc Chơi JavaScript
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.