Nitro + H3: بناء واجهات برمجة تطبيقات TypeScript شاملة تعمل في كل مكان

قاعدة كود واحدة. كل بيئة تشغيل. Nitro هو محرك الخادم مفتوح المصدر المبني بـ TypeScript الذي يعمل خلف Nuxt و Analog و Vinxi. يقوم بتجميع مسارات API الخاصة بك إلى حزم محسّنة لـ Node.js و Cloudflare Workers و Vercel Edge و Deno و Bun والمزيد — دون تغيير أي إعداد. في هذا الدرس، ستبني واجهة برمجة تطبيقات REST كاملة وتنشرها في كل مكان.
ما ستتعلمه
بنهاية هذا الدرس، ستكون قادراً على:
- إعداد مشروع Nitro مستقل من الصفر مع TypeScript
- بناء مسارات API مع طلبات واستجابات محددة النوع باستخدام H3
- إنشاء برمجيات وسيطة للمصادقة والتسجيل و CORS
- تكامل قاعدة بيانات باستخدام طبقة التخزين المدمجة في Nitro
- تنفيذ نقاط WebSocket للميزات الفورية
- إضافة مهام مجدولة (cron jobs) تعمل على جانب الخادم
- استخدام إضافات الخادم وخطافات دورة الحياة
- نشر نفس قاعدة الكود على Node.js و Cloudflare Workers و Vercel و Deno
المتطلبات الأساسية
قبل البدء، تأكد من توفر:
- Node.js 20+ مثبت (
node --version) - خبرة في TypeScript (الأنواع، async/await)
- مفاهيم REST API الأساسية (طرق HTTP، رموز الحالة)
- محرر أكواد — VS Code أو Cursor موصى بهما
- حساب Cloudflare (اختياري، للنشر على Workers)
لماذا Nitro؟
معظم أطر عمل خوادم TypeScript تقيّدك ببيئة تشغيل واحدة. Express يعمل على Node.js فقط. Hono يستهدف بيئات الحافة. Nitro مختلف — يقوم بتجميع كود الخادم الخاص بك إلى حزم محسّنة لـ أي بيئة تشغيل JavaScript:
| الميزة | Nitro | Express | Hono | Fastify |
|---|---|---|---|---|
| متعدد البيئات | أكثر من 15 إعداد مسبق | Node.js فقط | جزئي | Node.js فقط |
| توجيه قائم على الملفات | مدمج | يدوي | يدوي | يدوي |
| استيراد تلقائي | نعم | لا | لا | لا |
| تخزين مدمج | KV, FS, Redis, D1 | يدوي | يدوي | يدوي |
| WebSockets | مدمج | حزمة ws | محول | إضافة |
| مهام مجدولة | مدمج | node-cron | غير مدمج | غير مدمج |
| إزالة الكود غير المستخدم | تلقائي | لا | لا | لا |
| إعادة تحميل فوري | خادم تطوير مدمج | nodemon | يدوي | يدوي |
Nitro مدعوم بـ H3، إطار عمل HTTP مصغّر مبني للأداء العالي. H3 يتعامل مع تحليل الطلبات والتوجيه والاستجابة — بينما يضيف Nitro التوجيه القائم على الملفات والاستيراد التلقائي وتحسين البناء وإعدادات النشر المسبقة.
الخطوة 1: إنشاء مشروع Nitro جديد
أنشئ مشروع Nitro جديد:
npx giget@latest nitro nitro-api && cd nitro-api
npm installهيكل المشروع يبدو هكذا:
nitro-api/
├── routes/
│ └── index.ts # GET /
├── nitro.config.ts # إعدادات Nitro
├── tsconfig.json
└── package.json
شغّل خادم التطوير:
npx nitropack devزر http://localhost:3000 — سترى الاستجابة الافتراضية. خادم التطوير يحتوي على إعادة تحميل الوحدات الفوري مدمج، لذا التغييرات تظهر فوراً.
الخطوة 2: بناء مسارات API مع H3
Nitro يستخدم التوجيه القائم على الملفات. كل ملف في مجلد routes/ يصبح نقطة نهاية API. مسار الملف يُطابق مباشرة مسار URL.
المسارات الأساسية
أنشئ routes/api/health.ts:
export default defineEventHandler(() => {
return { status: "ok", timestamp: Date.now() };
});يستجيب لـ GET /api/health. Nitro يستورد تلقائياً defineEventHandler — لا حاجة لجملة استيراد.
معاملات المسار
أنشئ routes/api/users/[id].ts:
export default defineEventHandler((event) => {
const id = getRouterParam(event, "id");
return {
user: {
id,
name: `User ${id}`,
email: `user${id}@example.com`,
},
};
});الوصول إليه عبر GET /api/users/42. [id] في اسم الملف يصبح معامل ديناميكي.
التوجيه حسب طريقة HTTP
أنشئ معالجات خاصة بكل طريقة عبر تسمية الملفات بلاحقة الطريقة:
routes/
└── api/
└── posts/
├── index.get.ts # GET /api/posts
├── index.post.ts # POST /api/posts
└── [id].put.ts # PUT /api/posts/:id
└── [id].delete.ts # DELETE /api/posts/:id
أنشئ routes/api/posts/index.get.ts:
export default defineEventHandler(() => {
return {
posts: [
{ id: 1, title: "البدء مع Nitro", published: true },
{ id: 2, title: "H3 بالتفصيل", published: false },
],
};
});أنشئ routes/api/posts/index.post.ts:
export default defineEventHandler(async (event) => {
const body = await readBody(event);
if (!body.title) {
throw createError({
statusCode: 400,
statusMessage: "العنوان مطلوب",
});
}
return {
post: {
id: Math.floor(Math.random() * 1000),
title: body.title,
content: body.content || "",
createdAt: new Date().toISOString(),
},
};
});التحقق من الطلبات مع الأنواع
للتحقق القوي من المدخلات، استخدم readValidatedBody من H3 مع مكتبة تحقق:
npm install zodأنشئ routes/api/posts/index.post.ts مع تحقق Zod:
import { z } from "zod";
const PostSchema = z.object({
title: z.string().min(3).max(200),
content: z.string().optional().default(""),
tags: z.array(z.string()).optional().default([]),
published: z.boolean().optional().default(false),
});
export default defineEventHandler(async (event) => {
const body = await readValidatedBody(event, PostSchema.parse);
return {
post: {
id: Math.floor(Math.random() * 1000),
...body,
createdAt: new Date().toISOString(),
},
};
});إذا فشل التحقق، H3 يُرجع تلقائياً خطأ 400 مع التفاصيل.
الخطوة 3: البرمجيات الوسيطة والأدوات المساعدة
برمجية وسيطة للطلبات
أنشئ ملفات في مجلد middleware/ تعمل قبل كل معالج مسار.
أنشئ middleware/logger.ts:
export default defineEventHandler((event) => {
const method = getMethod(event);
const path = getRequestURL(event).pathname;
console.log(`[${new Date().toISOString()}] ${method} ${path}`);
});يسجل كل طلب وارد. معالجات البرمجيات الوسيطة التي لا تُرجع قيمة تمرر التحكم للمعالج التالي.
برمجية وسيطة خاصة بالمسار
أنشئ middleware/api/auth.ts — تعمل فقط لمسارات /api/*:
export default defineEventHandler((event) => {
const authHeader = getHeader(event, "authorization");
if (!authHeader?.startsWith("Bearer ")) {
throw createError({
statusCode: 401,
statusMessage: "رأس التفويض مفقود أو غير صالح",
});
}
const token = authHeader.slice(7);
// في الإنتاج، تحقق من JWT هنا
event.context.userId = token;
});الوصول لمعرف المستخدم في أي مسار API عبر event.context.userId.
إعداد CORS
Nitro يدعم CORS مدمجاً. حدّث nitro.config.ts:
export default defineNitroConfig({
routeRules: {
"/api/**": {
cors: true,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
},
},
},
});الأدوات المساعدة المشتركة
أنشئ أدوات قابلة لإعادة الاستخدام في مجلد utils/. يتم استيرادها تلقائياً في كل المشروع.
أنشئ utils/response.ts:
export function successResponse<T>(data: T, message = "نجاح") {
return {
success: true,
message,
data,
};
}
export function paginatedResponse<T>(
data: T[],
page: number,
limit: number,
total: number
) {
return {
success: true,
data,
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
},
};
}استخدمها في أي مسار دون استيراد:
export default defineEventHandler(() => {
return successResponse({ version: "1.0.0" });
});الخطوة 4: تكامل قاعدة البيانات مع طبقة التخزين
Nitro يتضمن طبقة تخزين شاملة مدعومة بـ unstorage. توفر واجهة مفتاح-قيمة تعمل مع عدة خلفيات — نظام الملفات، Redis، Cloudflare KV، Vercel KV، والمزيد.
إعداد التخزين
حدّث nitro.config.ts:
export default defineNitroConfig({
storage: {
posts: {
driver: "fs",
base: ".data/posts",
},
cache: {
driver: "memory",
},
},
});استخدام التخزين في المسارات
أنشئ routes/api/posts/index.get.ts:
export default defineEventHandler(async () => {
const keys = await useStorage("posts").getKeys();
const posts = await Promise.all(
keys.map(async (key) => {
return await useStorage("posts").getItem(key);
})
);
return successResponse(posts.filter(Boolean));
});أنشئ routes/api/posts/index.post.ts:
import { z } from "zod";
const PostSchema = z.object({
title: z.string().min(3),
content: z.string(),
tags: z.array(z.string()).default([]),
});
export default defineEventHandler(async (event) => {
const body = await readValidatedBody(event, PostSchema.parse);
const id = `post-${Date.now()}`;
const post = {
id,
...body,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
await useStorage("posts").setItem(id, post);
setResponseStatus(event, 201);
return successResponse(post, "تم إنشاء المقال");
});أنشئ routes/api/posts/[id].get.ts:
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, "id");
const post = await useStorage("posts").getItem(id!);
if (!post) {
throw createError({
statusCode: 404,
statusMessage: "المقال غير موجود",
});
}
return successResponse(post);
});أنشئ routes/api/posts/[id].delete.ts:
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, "id");
const exists = await useStorage("posts").hasItem(id!);
if (!exists) {
throw createError({
statusCode: 404,
statusMessage: "المقال غير موجود",
});
}
await useStorage("posts").removeItem(id!);
return successResponse(null, "تم حذف المقال");
});التبديل إلى Redis في الإنتاج
لاستخدام Redis بدلاً من نظام الملفات، غيّر إعداد التخزين:
export default defineNitroConfig({
storage: {
posts: {
driver: "redis",
url: process.env.REDIS_URL || "redis://localhost:6379",
},
},
});لا حاجة لتغيير أي كود في المسارات — واجهة التخزين نفسها بغض النظر عن الخلفية.
الخطوة 5: التخزين المؤقت مع قواعد المسارات
Nitro يحتوي على تخزين مؤقت مدمج. خزّن استجابات API مؤقتاً دون تغيير الكود باستخدام قواعد المسارات:
export default defineNitroConfig({
routeRules: {
"/api/posts": {
cache: {
maxAge: 60, // 60 ثانية
},
},
"/api/health": {
cache: {
maxAge: 10,
},
},
"/api/posts/**": {
cache: false, // بدون تخزين مؤقت للمقالات الفردية
},
},
});للتخزين المؤقت البرمجي، استخدم cachedEventHandler:
export default cachedEventHandler(
async () => {
// هذه العملية المكلفة مخزنة مؤقتاً
const allPosts = await fetchAllPosts();
const stats = computeStats(allPosts);
return successResponse(stats);
},
{
maxAge: 300, // 5 دقائق
name: "post-stats",
}
);الخطوة 6: دعم WebSocket
Nitro يدعم WebSockets من خلال أداة defineWebSocketHandler.
أنشئ routes/ws.ts:
export default defineWebSocketHandler({
open(peer) {
console.log(`[ws] عميل متصل: ${peer.id}`);
peer.send(JSON.stringify({ type: "welcome", id: peer.id }));
peer.subscribe("chat");
},
message(peer, message) {
const data = JSON.parse(message.text());
console.log(`[ws] رسالة من ${peer.id}:`, data);
// بث لجميع المشتركين
peer.publish(
"chat",
JSON.stringify({
type: "message",
from: peer.id,
text: data.text,
timestamp: Date.now(),
})
);
},
close(peer) {
console.log(`[ws] عميل انقطع: ${peer.id}`);
},
});فعّل دعم WebSocket في nitro.config.ts:
export default defineNitroConfig({
experimental: {
websocket: true,
},
});الاتصال من عميل:
const ws = new WebSocket("ws://localhost:3000/ws");
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("تم الاستلام:", data);
};
ws.onopen = () => {
ws.send(JSON.stringify({ text: "مرحباً من العميل!" }));
};الخطوة 7: المهام المجدولة
Nitro يدعم المهام المجدولة المشابهة لـ cron التي تعمل على جانب الخادم.
أنشئ tasks/cleanup.ts:
export default defineTask({
meta: {
name: "cleanup",
description: "إزالة البيانات المنتهية من التخزين",
},
async run() {
const keys = await useStorage("posts").getKeys();
let removed = 0;
for (const key of keys) {
const post: any = await useStorage("posts").getItem(key);
if (!post) continue;
const age = Date.now() - new Date(post.createdAt).getTime();
const thirtyDays = 30 * 24 * 60 * 60 * 1000;
if (age > thirtyDays && !post.published) {
await useStorage("posts").removeItem(key);
removed++;
}
}
return { result: `تمت إزالة ${removed} مسودة منتهية` };
},
});إعداد الجدولة في nitro.config.ts:
export default defineNitroConfig({
experimental: {
tasks: true,
},
scheduledTasks: {
// تشغيل التنظيف كل يوم عند منتصف الليل
"0 0 * * *": ["cleanup"],
},
});أثناء التطوير، شغّل المهام يدوياً عبر نقطة النهاية المدمجة:
curl http://localhost:3000/_nitro/tasks/cleanupالخطوة 8: إضافات الخادم ودورة الحياة
أنشئ إضافات تعمل عند بدء تشغيل الخادم. استخدمها لاتصالات قواعد البيانات أو تهيئة الخدمات أو التسجيل.
أنشئ plugins/startup.ts:
export default defineNitroPlugin((nitroApp) => {
console.log("[plugin] الخادم يبدأ التشغيل...");
// ربط بدورة حياة الطلب
nitroApp.hooks.hook("request", (event) => {
event.context.requestStartTime = Date.now();
});
nitroApp.hooks.hook("afterResponse", (event) => {
const duration = Date.now() - (event.context.requestStartTime || 0);
const path = getRequestURL(event).pathname;
console.log(`[perf] ${path} - ${duration}ms`);
});
// ربط بإغلاق الخادم
nitroApp.hooks.hook("close", () => {
console.log("[plugin] الخادم يتوقف...");
});
});الخطوة 9: معالجة الأخطاء
أنشئ معالج أخطاء عام لتوحيد استجابات الأخطاء.
أنشئ plugins/error-handler.ts:
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook("error", (error, { event }) => {
console.error(`[error] ${error.message}`, {
path: event ? getRequestURL(event).pathname : "unknown",
stack: error.stack,
});
});
});أنشئ استجابات خطأ مخصصة في المسارات:
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, "id");
if (!id || !/^\d+$/.test(id)) {
throw createError({
statusCode: 400,
statusMessage: "تنسيق المعرف غير صالح",
data: {
field: "id",
expected: "سلسلة رقمية",
received: id,
},
});
}
// متابعة المعالجة...
});الخطوة 10: النشر في كل مكان
هنا يتألق Nitro حقاً. نفس قاعدة الكود تُنشر على أي منصة بتغيير قيمة إعداد واحدة.
النشر على Node.js (الافتراضي)
npx nitropack build
node .output/server/index.mjsمخرجات البناء عبارة عن خادم مستقل في .output/ — لا حاجة لـ node_modules في الإنتاج.
النشر على Cloudflare Workers
حدّث nitro.config.ts:
export default defineNitroConfig({
preset: "cloudflare-pages",
});بناء ونشر:
npx nitropack build
npx wrangler pages deploy .output/publicالنشر على Vercel
export default defineNitroConfig({
preset: "vercel-edge",
});ادفع إلى Git — Vercel يكتشف تلقائياً ويُنشر.
النشر على Deno Deploy
export default defineNitroConfig({
preset: "deno-deploy",
});npx nitropack build
deployctl deploy --project=my-api .output/server/index.tsالنشر على Bun
export default defineNitroConfig({
preset: "bun",
});npx nitropack build
bun .output/server/index.mjsجميع الإعدادات المسبقة المتاحة
Nitro يدعم أكثر من 15 هدف نشر:
| الإعداد المسبق | المنصة |
|---|---|
node | خادم Node.js مستقل |
bun | بيئة تشغيل Bun |
deno-deploy | Deno Deploy |
cloudflare-pages | Cloudflare Pages |
cloudflare-module | Cloudflare Workers |
vercel | Vercel Serverless Functions |
vercel-edge | Vercel Edge Functions |
netlify | Netlify Functions |
netlify-edge | Netlify Edge Functions |
aws-lambda | AWS Lambda |
firebase | Firebase Functions |
digital-ocean | DigitalOcean App Platform |
render | Render.com |
docker | حاوية Docker |
الخطوة 11: إعداد الإنتاج
أنشئ إعداداً جاهزاً للإنتاج:
export default defineNitroConfig({
// هدف النشر
preset: process.env.NITRO_PRESET || "node",
// تخزين مؤقت على مستوى المسار
routeRules: {
"/api/**": {
cors: true,
headers: {
"Access-Control-Allow-Origin": "*",
},
},
"/api/posts": {
cache: { maxAge: 60 },
},
},
// خلفيات التخزين
storage: {
posts: {
driver: process.env.REDIS_URL ? "redis" : "fs",
...(process.env.REDIS_URL
? { url: process.env.REDIS_URL }
: { base: ".data/posts" }),
},
},
// إعدادات وقت التشغيل (الوصول إليها عبر useRuntimeConfig())
runtimeConfig: {
apiSecret: process.env.API_SECRET || "dev-secret",
public: {
apiBase: process.env.API_BASE || "http://localhost:3000",
},
},
// تفعيل الميزات التجريبية
experimental: {
websocket: true,
tasks: true,
},
// المهام المجدولة
scheduledTasks: {
"0 0 * * *": ["cleanup"],
},
// الضغط
compressPublicAssets: true,
});الوصول لإعدادات وقت التشغيل في المسارات:
export default defineEventHandler(() => {
const config = useRuntimeConfig();
return {
apiBase: config.public.apiBase,
// لا تكشف أبداً الأسرار في الاستجابات
};
});اختبار واجهة API
اختبر واجهة API الكاملة باستخدام curl:
# فحص الصحة
curl http://localhost:3000/api/health
# إنشاء مقال
curl -X POST http://localhost:3000/api/posts \
-H "Content-Type: application/json" \
-d '{"title": "مقالي الأول", "content": "مرحباً Nitro!", "tags": ["مقدمة"]}'
# عرض جميع المقالات
curl http://localhost:3000/api/posts
# الحصول على مقال محدد (استخدم المعرف من استجابة الإنشاء)
curl http://localhost:3000/api/posts/post-1712140800000
# حذف مقال
curl -X DELETE http://localhost:3000/api/posts/post-1712140800000استكشاف الأخطاء وإصلاحها
أخطاء "Cannot find module"
Nitro يستورد تلقائياً الأدوات من H3 ومن بيئة تشغيله. إذا أظهر محررك أخطاء، تأكد أن tsconfig.json يمتد من الأنواع المولّدة:
{
"extends": "./.nitro/types/tsconfig.json"
}شغّل npx nitropack prepare لتوليد الأنواع.
التخزين لا يحفظ البيانات
في التطوير، مشغّل نظام الملفات يخزن البيانات في .data/. تأكد أن هذا المجلد موجود وقابل للكتابة. للإنتاج، استخدم Redis أو مشغّل تخزين سحابي.
رفض اتصال WebSocket
تأكد أن experimental.websocket مضبوط على true في nitro.config.ts. بعض منصات النشر (مثل Cloudflare Pages) تتطلب إعداداً خاصاً لدعم WebSocket.
مخرجات البناء كبيرة جداً
Nitro يزيل الكود غير المستخدم تلقائياً. إذا كانت المخرجات لا تزال كبيرة، تحقق من التبعيات الثقيلة المستوردة على المستوى الأعلى. استخدم الاستيراد الديناميكي للميزات الاختيارية:
export default defineEventHandler(async () => {
const { heavyLibrary } = await import("heavy-library");
return heavyLibrary.process();
});الخطوات التالية
- أضف قاعدة بيانات: اربط Drizzle ORM أو Prisma لدعم قواعد بيانات SQL
- أضف مصادقة: نفّذ التحقق من JWT في البرمجيات الوسيطة
- أضف تحديد المعدل: استخدم طبقة التخزين المؤقت في Nitro مع
unstorageلتحديد المعدل - ابنِ واجهة أمامية: اقرنه مع أي إطار عمل واجهة أمامية — React، Vue، Svelte، أو HTML عادي
- استكشف Nuxt: إذا كنت بحاجة لإطار عمل متكامل، Nuxt يستخدم Nitro كمحرك خادمه بنفس الواجهات
الخلاصة
Nitro و H3 يمنحانك ميزة فريدة في منظومة خوادم TypeScript: اكتب مرة واحدة، انشر في أي مكان. بنيت واجهة برمجة تطبيقات REST كاملة مع تحقق محدد النوع، برمجيات وسيطة، تخزين مؤقت، WebSockets، مهام مجدولة، وطبقة تخزين شاملة. نفس قاعدة الكود تعمل على Node.js و Cloudflare Workers و Vercel Edge و Deno و Bun دون تغيير سطر واحد من كود التطبيق.
المفاهيم الأساسية التي تعلمتها:
- التوجيه القائم على الملفات يربط مسارات نظام الملفات بنقاط نهاية API
- أدوات H3 توفر معالجة طلبات/استجابات محددة النوع
- طبقة التخزين تجرّد خلفية قاعدة البيانات
- قواعد المسارات تضبط التخزين المؤقت والرؤوس بشكل تصريحي
- إعدادات النشر المسبقة تجمّع كودك لأي بيئة تشغيل
Nitro مُثبت في الإنتاج — يخدم ملايين الطلبات يومياً كمحرك وراء تطبيقات Nuxt حول العالم. سواء كنت تبني خدمة مصغّرة أو واجهة برمجة تطبيقات REST أو الخلفية لتطبيق متكامل، Nitro يضمن أن كود خادمك قابل للنقل وعالي الأداء ومستعد للمستقبل.
ناقش مشروعك معنا
نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.
دعنا نجد أفضل الحلول لاحتياجاتك.
مقالات ذات صلة

بناء ونشر واجهة برمجية بدون خوادم باستخدام Cloudflare Workers و Hono و D1
تعلّم كيف تبني واجهة برمجية REST جاهزة للإنتاج باستخدام Cloudflare Workers وإطار Hono وقاعدة بيانات D1 — من إعداد المشروع حتى النشر العالمي.

بناء واجهة GraphQL آمنة الأنواع مع Next.js App Router و Yoga و Pothos
تعلم كيفية بناء واجهة GraphQL API آمنة الأنواع بالكامل باستخدام Next.js 15 App Router و GraphQL Yoga و Pothos schema builder. يغطي هذا الدليل العملي تصميم المخططات والاستعلامات والتحولات والمصادقة وعميل React باستخدام urql.

واجهة Mistral AI مع TypeScript: بناء تطبيقات ذكية
تعلّم كيفية استخدام واجهة Mistral AI مع TypeScript لبناء تطبيقات ذكية: محادثة، توليد منظّم، استدعاء دوال، وRAG.