عند بناء API حديث في TypeScript، اختيار ORM يغير جودة حياة المطور جذرياً. Drizzle ORM أصبح في 2026 الخيار المهيمن لمشاريع Bun و Node.js الحديثة: type-safe من البداية للنهاية، بدون توليد كود، تركيب قريب من SQL، دعم أصلي PostgreSQL.
راجع دليل Bun.
لماذا Drizzle؟
- بدون codegen: كل شيء مستنتج من أنواع TypeScript
- Type-safety كامل: SELECT، JOIN، INSERT تعيد أنواعاً محسوبة
- تركيب قريب من SQL
- خفيف: ~10 KB gzip vs 200+ KB لـ Prisma
- Migrations عبر drizzle-kit
- أداء ممتاز: لا RPC داخلي، فقط SQL
الخطوة 1 — التثبيت
bun add drizzle-orm postgres
bun add -D drizzle-kit @types/bun
الخطوة 2 — تعريف المخطط
import { pgTable, serial, varchar, integer, timestamp, boolean } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
email: varchar("email", { length: 255 }).notNull().unique(),
name: varchar("name", { length: 100 }).notNull(),
passwordHash: varchar("password_hash", { length: 255 }).notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
الخطوة 3 — العميل
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
const client = postgres(process.env.DATABASE_URL!, {
max: 10,
prepare: false, // مهم لـ PgBouncer transaction mode
});
export const db = drizzle(client);
الخطوة 4 — Migrations
// 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! },
});
// أوامر
bun drizzle-kit generate
bun drizzle-kit migrate
الخطوة 5 — استعلامات
import { eq, and, desc } from "drizzle-orm";
const allUsers = await db.select().from(users);
const aissatou = await db.select().from(users)
.where(eq(users.email, "a@exemple.sn"));
const [newUser] = await db.insert(users).values({
email: "modou@exemple.sn",
name: "Modou",
passwordHash: await Bun.password.hash("secret"),
}).returning();
await db.update(users).set({ name: "Modou D." })
.where(eq(users.id, newUser.id));
await db.delete(users).where(eq(users.id, newUser.id));
الخطوة 6 — JOIN ومعاملات
// JOIN
const postsWithAuthors = await db
.select({
title: posts.title,
authorName: users.name,
})
.from(posts)
.leftJoin(users, eq(posts.authorId, users.id))
.where(eq(posts.published, true));
// معاملة atomic
await db.transaction(async (tx) => {
const [user] = await tx.insert(users).values({...}).returning();
await tx.insert(posts).values({ authorId: user.id, ... });
});
الخطوة 7 — في الإنتاج
- migrations في CI/CD قبل النشر
- متغير DATABASE_URL وحده
- ضبط max pool حسب عدد الحاويات
- pg_stat_statements للمراقبة
- PgBouncer مع prepare: false
التكييف
Drizzle له ميزة خاصة للمشاريع الصغيرة: استهلاك ذاكرة أدنى وسرعة تناسب VPS متواضع. على نفس hardware، Drizzle + Bun عند 80-100 MB، Prisma + Node عند 200-300 MB.
أخطاء شائعة
| الخطأ | السبب | الحل |
|---|---|---|
| SSL required | Postgres يفرض SSL | postgres(url, { ssl: ‘require’ }) |
| any غير متوقع | schema غير مستورد | drizzle(client, { schema }) |
| أداء يتدهور مع PgBouncer | prepare: true | prepare: false |