Bun هو runtime JavaScript بديل لـ Node.js، أسرع 3-5× ومدمج مع package manager وbundler. Drizzle ORM هو TypeScript-first ORM يفوز بسرعته وحجمه الصغير على Prisma. دمجهما مع PostgreSQL ينتج stack حديث جداً للـ APIs والتطبيقات في 2026. هذا الدرس يبني نظاماً كاملاً في 8 خطوات.
المتطلبات
- Linux/macOS (Bun لا يدعم Windows أصلياً، يحتاج WSL)
- PostgreSQL متاح (محلي عبر Docker، أو Cloud)
- أساسيات TypeScript
- الوقت المقدر: ساعة ونصف
الخطوة 1 — تثبيت Bun
# Linux/macOS:
curl -fsSL https://bun.sh/install | bash
# تحقق:
bun --version # → 1.1+
# WSL (Windows):
wsl curl -fsSL https://bun.sh/install | bash
Bun 1.1+ (مايو 2024) هو الحد الأدنى المستقر. النسخ الأحدث (1.2+ في 2026) تجلب ميزات Bun الجديدة: Bun Test، Bun:sqlite، Bun.serve محسّن. Bun يحلّ محل: Node + npm + pnpm + esbuild + ts-node، كل ذلك في binary واحد بـ 50 MB.
الخطوة 2 — مشروع جديد
mkdir my-api && cd my-api
bun init
# اختر TypeScript template
bun add drizzle-orm postgres
bun add -d drizzle-kit @types/pg
bun add hono # web framework خفيف، يعمل بسلاسة مع Bun
Hono هو web framework صغير (12 KB) وسريع، صُمم للـ runtimes الحديثة (Bun، Deno، Cloudflare Workers). أسرع من Express بـ 2-3×. مع Bun، يعطي أحد أسرع HTTP servers على الإطلاق: 200K+ req/sec على VPS متوسط. للـ APIs الحرجة، هذا stack يلغي الحاجة لـ Go أو Rust.
الخطوة 3 — Schema بـ Drizzle
Drizzle يستخدم TypeScript للتعريف، ينتج SQL آلياً، يحافظ على type safety كاملة من قاعدة البيانات إلى الـ API.
// src/db/schema.ts
import { pgTable, serial, text, timestamp, boolean, bigint } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
email: text("email").notNull().unique(),
name: text("name").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
export const posts = pgTable("posts", {
id: bigint("id", { mode: "number" }).generatedAlwaysAsIdentity().primaryKey(),
authorId: serial("author_id").references(() => users.id),
title: text("title").notNull(),
content: text("content"),
published: boolean("published").default(false),
publishedAt: timestamp("published_at"),
});
الفائدة الكبرى: typeof users.$inferSelect يعطيك الـ TypeScript type تلقائياً. لا حاجة لـ generated code، كل شيء type-safe في الوقت الفعلي. تغيير العمود في schema.ts → الـ types في كل الـ codebase تحدّث فوراً.
الخطوة 4 — Migrations
Drizzle Kit يولّد ملفات SQL migration من تغييرات الـ schema. لا تكتب SQL يدوياً.
// drizzle.config.ts
import type { Config } from "drizzle-kit";
export default {
schema: "./src/db/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: { url: process.env.DATABASE_URL! },
} satisfies Config;
# أوامر:
bun drizzle-kit generate # ينشئ migration من تغيير الـ schema
bun drizzle-kit migrate # يطبّق migrations على قاعدة البيانات
bun drizzle-kit studio # GUI لتصفح القاعدة (drizzle studio)
bun drizzle-kit push # للتطوير: يحدّث القاعدة بدون migration
Drizzle Studio (مثل Prisma Studio لكن أخف) يفتح GUI في المتصفح لتصفح وتعديل البيانات. مفيد جداً للتطوير. للإنتاج، استخدم migrations دائماً (لا push) — تركز التغييرات في git، تطبّق على staging قبل production.
الخطوة 5 — Queries
// 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 });
// queries مع type safety كاملة:
import { eq, and, gt } from "drizzle-orm";
import { db } from "./db";
import { users, posts } from "./db/schema";
// جلب مستخدم
const user = await db.select().from(users).where(eq(users.id, 1));
// user[0] type: { id, email, name, createdAt }
// JOIN مع where مركّب:
const publishedPosts = await db
.select({ title: posts.title, authorName: users.name })
.from(posts)
.innerJoin(users, eq(posts.authorId, users.id))
.where(and(
eq(posts.published, true),
gt(posts.publishedAt, new Date("2026-01-01"))
));
Drizzle queries تشبه SQL أكثر من ORM التقليدي. هذا قصد: من يعرف SQL، يكتب Drizzle بسرعة. الـ type inference يعمل في كل مكان: publishedPosts[0].title و publishedPosts[0].authorName موصوفان من الـ select. أخطاء الكتابة تُكشف في الـ compile time.
الخطوة 6 — API بـ Hono
// src/index.ts
import { Hono } from "hono";
import { db } from "./db";
import { users } from "./db/schema";
import { eq } from "drizzle-orm";
const app = new Hono();
app.get("/", (c) => c.json({ status: "ok" }));
app.get("/users/:id", async (c) => {
const id = parseInt(c.req.param("id"));
const user = await db.select().from(users).where(eq(users.id, id));
if (!user.length) return c.json({ error: "not found" }, 404);
return c.json(user[0]);
});
app.post("/users", async (c) => {
const body = await c.req.json<{ email: string; name: string }>();
const [created] = await db.insert(users).values(body).returning();
return c.json(created, 201);
});
export default { port: 3000, fetch: app.fetch };
تشغيل: bun run src/index.ts. السيرفر يبدأ على :3000. Hono + Bun = سريع جداً (5-10× أسرع من Express + Node). للـ production، أضف middleware: CORS، rate limiting (Hono يدعم نشط)، logging، error handling. Hono Docs بـ 400+ مثال.
الخطوة 7 — Validation وZod
أمن أساسي: لا تثق ببيانات المستخدم. Zod مع Hono يفلتر الـ body قبل وصوله لقاعدة البيانات.
bun add zod @hono/zod-validator
import { z } from "zod";
import { zValidator } from "@hono/zod-validator";
const userSchema = z.object({
email: z.string().email(),
name: z.string().min(2).max(100),
});
app.post(
"/users",
zValidator("json", userSchema),
async (c) => {
const body = c.req.valid("json"); // type-safe الآن
const [created] = await db.insert(users).values(body).returning();
return c.json(created, 201);
}
);
// طلب بإيميل غير صحيح → response 400 تلقائياً
// مع تفاصيل الأخطاء بصيغة JSON
Zod يحلّ مشكلة validation برشاقة: تعرّف الـ schema مرة، تحصل على validation + TypeScript types. الـ schema يصير « single source of truth » — تغيير حقل في الـ schema، الـ validation و الـ types يتحدّثان تلقائياً. هذا الـ pattern (DDD، single source) يقلّل bugs بشكل ملحوظ.
الخطوة 8 — النشر للإنتاج
| المنصة | السعر | الميزة |
|---|---|---|
| VPS + Coolify | 5-20 USD | تحكم كامل |
| Fly.io | 0-25 USD | multi-region، scaling |
| Railway | 5+ USD | سهل، CD/CI مدمج |
| Cloudflare Workers | 0-5 USD/M req | edge compute عالمي |
# Dockerfile لـ Bun:
FROM oven/bun:1.2-alpine AS base
WORKDIR /app
FROM base AS deps
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
FROM base AS builder
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN bun run build
FROM oven/bun:1.2-alpine AS runner
COPY --from=builder /app/dist ./dist
CMD ["bun", "run", "dist/index.js"]
EXPOSE 3000
image size بـ Bun ~ 100 MB، أصغر بـ 3× من Node. سرعة الإقلاع: 200 ms مقابل 500-800 ms لـ Node. هذا مهم في الـ serverless حيث cold starts كثيرة. للـ Cloudflare Workers، استخدم hono مباشرة بدون Bun (Cloudflare يستخدم runtime خاص).
أخطاء شائعة
| المشكلة | السبب | الحل |
|---|---|---|
| Bun يفشل على Windows | دعم Native غير مكتمل | استخدم WSL2 |
| Drizzle types خاطئة | tsconfig قديم | strict: true إلزامي |
| Migrations معلّقة | oid في drizzle.config مفقود | أعد تشغيل drizzle-kit |
| Connection pool ينفجر | اتصال جديد كل request | استخدم postgres-js مع max=10 |
| « Bun is not Node » | module يتطلب Node APIs | تحقق من توافق Bun |
| Hono routes لا تعمل | ترتيب middleware | middleware قبل routes |
للمزيد
- Bun Docs bun.sh/docs
- Drizzle ORM orm.drizzle.team
- Hono hono.dev
- Zod zod.dev