Backالعودة إلى المدونة

بناء واجهات برمجة التطبيقات (APIs) باستخدام Next.js

تعرف على كيفية بناء واجهات برمجة التطبيقات (APIs) باستخدام Next.js.

سيتناول هذا الدليل كيفية بناء واجهات برمجة التطبيقات (APIs) باستخدام Next.js، بما في ذلك إعداد المشروع، فهم موجه التطبيق (App Router) ومعالجات المسارات (Route Handlers)، التعامل مع طرق HTTP المتعددة، تنفيذ التوجيه الديناميكي، إنشاء منطق وسيط قابل لإعادة الاستخدام، واتخاذ قرار بشأن الوقت المناسب لإنشاء طبقة API مخصصة.

1. البدء

1.1 إنشاء تطبيق Next.js

إذا كنت تبدأ من الصفر، يمكنك إنشاء مشروع Next.js جديد باستخدام:

Terminal
npx create-next-app@latest --api

ملاحظة: تحدد العلامة --api تلقائيًا مثالًا لملف route.ts في مجلد app/ الخاص بمشروعك الجديد، لتوضيح كيفية إنشاء نقطة نهاية لواجهة برمجة التطبيقات.

1.2 موجه التطبيق (App Router) مقابل موجه الصفحات (Pages Router)

  • موجه الصفحات (Pages Router): تاريخيًا، استخدم Next.js pages/api/* لواجهات برمجة التطبيقات. اعتمد هذا النهج على كائنات طلب/استجابة Node.js وواجهة برمجة تطبيقات تشبه Express.
  • موجه التطبيق (App Router) (الافتراضي): تم تقديمه في Next.js 13، يعتمد موجه التطبيق بالكامل على واجهات برمجة تطبيقات طلب/استجابة الويب القياسية. بدلاً من pages/api/*، يمكنك الآن وضع ملفات route.ts أو route.js في أي مكان داخل دليل app/.

لماذا التبديل؟ تعتمد "معالجات المسارات (Route Handlers)" في موجه التطبيق على واجهات برمجة تطبيقات طلب/استجابة منصة الويب بدلاً من واجهات برمجة تطبيقات محددة لـ Node.js. هذا يبسط التعلم، ويقلل الاحتكاك، ويساعدك على إعادة استخدام معرفتك عبر أدوات مختلفة.

2. لماذا (ومتى) تبني واجهات برمجة التطبيقات باستخدام Next.js

  1. واجهة برمجة تطبيقات عامة لعملاء متعددين

    • يمكنك بناء واجهة برمجة تطبيقات عامة يتم استهلاكها بواسطة تطبيق الويب Next.js الخاص بك، أو تطبيق جوال منفصل، أو أي خدمة تابعة لجهة خارجية. على سبيل المثال، قد تجلب البيانات من /api/users في كل من موقع React الخاص بك وتطبيق React Native للجوال.
  2. وكيل لخلفية موجودة

    • في بعض الأحيان تريد إخفاء أو دمج الخدمات المصغرة (microservices) الخارجية خلف نقطة نهاية واحدة. يمكن أن تعمل معالجات المسارات (Route Handlers) في Next.js كطبقة وسيطة أو وكيل لخلفية موجودة. على سبيل المثال، يمكنك اعتراض الطلبات، والتعامل مع المصادقة، وتحويل البيانات، ثم تمرير الطلب إلى واجهة برمجة تطبيقات المصدر.
  3. الخطافات الويب (Webhooks) والتكاملات

    • إذا كنت تتلقى ردود اتصال أو خطافات ويب خارجية (مثل من Stripe أو GitHub أو Twilio)، فيمكنك التعامل معها باستخدام معالجات المسارات (Route Handlers).
  4. مصادقة مخصصة

    • إذا كنت بحاجة إلى جلسات أو رموز أو منطق مصادقة آخر، فيمكنك تخزين ملفات تعريف الارتباط، وقراءة الرؤوس، والرد بالبيانات المناسبة في طبقة واجهة برمجة التطبيقات Next.js الخاصة بك.

ملاحظة: إذا كنت تحتاج فقط إلى جلب بيانات من جانب الخادم لتطبيق Next.js الخاص بك (ولا تحتاج إلى مشاركة هذه البيانات خارجيًا)، فقد تكون مكونات الخادم (Server Components) كافية لجلب البيانات مباشرة أثناء العرض - ولا حاجة إلى طبقة واجهة برمجة تطبيقات منفصلة.

3. إنشاء واجهة برمجة تطبيقات باستخدام معالجات المسارات (Route Handlers)

3.1 إعداد الملف الأساسي

في موجه التطبيق (app/)، أنشئ مجلدًا يمثل مسارك، وداخله ملف route.ts.

على سبيل المثال، لإنشاء نقطة نهاية عند /api/users:

app
└── api
    └── users
        └── route.ts

3.2 طرق HTTP متعددة في ملف واحد

على عكس مسارات واجهة برمجة التطبيقات في موجه الصفحات (التي كان لها تصدير افتراضي واحد)، يمكنك تصدير دوال متعددة تمثل طرق HTTP مختلفة من نفس الملف.

app/api/users/route.ts
export async function GET(request: Request) {
  // على سبيل المثال، جلب البيانات من قاعدة البيانات هنا
  const users = [
    { id: 1, name: 'أليس' },
    { id: 2, name: 'بوب' }
  ];
  return new Response(JSON.stringify(users), {
    status: 200,
    headers: { 'Content-Type': 'application/json' }
  });
}
 
export async function POST(request: Request) {
  // تحليل جسم الطلب
  const body = await request.json();
  const { name } = body;
 
  // مثلاً، إدراج مستخدم جديد في قاعدة البيانات
  const newUser = { id: Date.now(), name };
 
  return new Response(JSON.stringify(newUser), {
    status: 201,
    headers: { 'Content-Type': 'application/json' }
  });
}

الآن، إرسال طلب GET إلى /api/users سيعيد قائمة المستخدمين الخاصة بك، بينما طلب POST إلى نفس العنوان URL سيدرج مستخدمًا جديدًا.

4. العمل مع واجهات برمجة تطبيقات الويب (Web APIs)

4.1 استخدام Request و Response مباشرة

بشكل افتراضي، تتلقى طرق معالج المسار (GET، POST، إلخ) كائن Request قياسي، ويجب عليك إرجاع كائن Response قياسي.

4.2 معاملات الاستعلام (Query parameters)

app/api/search/route.ts
import { NextRequest } from 'next/server';
 
export function GET(request: NextRequest) {
  const searchParams = request.nextUrl.searchParams;
  const query = searchParams.get('query'); // مثلاً `/api/search?query=مرحبا`
 
  return new Response(
    JSON.stringify({ result: `لقد بحثت عن: ${query}` }),
    {
      headers: { 'Content-Type': 'application/json' },
    },
  );
}

4.3 الرؤوس (Headers) وملفات تعريف الارتباط (Cookies)

app/api/auth/route.ts
import { NextRequest } from 'next/server';
import { cookies, headers } from 'next/headers';
 
export async function GET(request: NextRequest) {
  // 1. استخدام أدوات 'next/headers'
  const cookieStore = await cookies();
  const token = cookieStore.get('token');
 
  const headersList = await headers();
  const referer = headersList.get('referer');
 
  // 2. استخدام واجهات برمجة تطبيقات الويب القياسية
  const userAgent = request.headers.get('user-agent');
 
  return new Response(JSON.stringify({ token, referer, userAgent }), {
    headers: { 'Content-Type': 'application/json' },
  });
}

يمكن أن تكون الدوال cookies() و headers() مفيدة إذا كنت تخطط لإعادة استخدام المنطق المشترك عبر كود جانب الخادم الآخر في Next.js. ستلاحظ أن Next.js يوفر أيضًا NextRequest و NextResponse اللذين يمتدان واجهات برمجة تطبيقات الويب الأساسية.

5. المسارات الديناميكية

لإنشاء مسارات ديناميكية (مثل /api/users/:id)، استخدم مقاطع ديناميكية في بنية مجلدك:

app
└── api
    └── users
        └── [id]
            └── route.ts

يتوافق هذا الملف مع عنوان URL مثل /api/users/123، حيث يتم التقاط 123 كمعامل.

app/api/users/[id]/route.ts
import { NextRequest } from 'next/server';
 
export async function GET(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> },
) {
  const id = (await params).id;
  // مثلاً، استعلام قاعدة بيانات عن مستخدم بالمعرف `id`
  return new Response(JSON.stringify({ id, name: `المستخدم ${id}` }), {
    status: 200,
    headers: { 'Content-Type': 'application/json' },
  });
}
 
export async function DELETE(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> },
) {
  const id = (await params).id;
  // مثلاً، حذف مستخدم بالمعرف `id` من قاعدة البيانات
  return new Response(null, { status: 204 });
}

هنا، يعطيك params.id المقطع الديناميكي.

6. استخدام Next.js كطبقة وكيل (proxy) أو توجيه

سيناريو شائع هو التصرف كوسيط لخدمة خلفية موجودة. يمكنك مصادقة الطلبات، والتعامل مع التسجيل، أو تحويل البيانات قبل إرسالها إلى خادم بعيد أو خلفية:

app/api/external/route.ts
import { NextRequest } from 'next/server';
 
export async function GET(request: NextRequest) {
  const response = await fetch('https://example.com/api/data', {
    // اختياري: توجيه بعض الرؤوس، إضافة رموز مصادقة، إلخ.
    headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
  });
 
  // تحويل أو توجيه الاستجابة
  const data = await response.json();
  const transformed = { ...data, source: 'تم تمريره عبر-nextjs' };
 
  return new Response(JSON.stringify(transformed), {
    headers: { 'Content-Type': 'application/json' },
  });
}

الآن يحتاج عملاؤك فقط إلى استدعاء /api/external، وسيتعامل Next.js مع الباقي. يُطلق على هذا أحيانًا اسم "الخلفية للواجهة الأمامية" أو BFF.

7. بناء منطق "وسيط" مشترك

إذا كنت تريد تطبيق نفس المنطق (مثل فحوصات المصادقة، التسجيل) عبر معالجات مسارات متعددة، يمكنك إنشاء دوال قابلة لإعادة الاستخدام تغلف معالجاتك:

lib/with-auth.ts
import { NextRequest } from 'next/server';
 
type Handler = (req: NextRequest, context?: any) => Promise<Response>;
 
export function withAuth(handler: Handler): Handler {
  return async (req, context) => {
    const token = req.cookies.get('token')?.value;
    if (!token) {
      return new Response(JSON.stringify({ error: 'غير مصرح' }), {
        status: 401,
        headers: { 'Content-Type': 'application/json' },
      });
    }
 
    // إذا تمت المصادقة، استدع المعالج الأصلي
    return handler(req, context);
  };
}

ثم في معالج المسار الخاص بك:

app/api/secret/route.ts
import { NextRequest } from 'next/server';
import { withAuth } from '@/lib/with-auth';
 
async function secretGET(request: NextRequest) {
  return new Response(JSON.stringify({ secret: 'هنا توجد تنانين' }), {
    headers: { 'Content-Type': 'application/json' },
  });
}
 
export const GET = withAuth(secretGET);

8. اعتبارات النشر ووضع "تطبيق الصفحة الواحدة (SPA)"

8.1 النشر القياسي لـ Node.js

يتيح لك نشر خادم Next.js القياسي باستخدام next start استخدام ميزات مثل معالجات المسارات (Route Handlers)، ومكونات الخادم (Server Components)، والوسيط (Middleware) والمزيد - مع الاستفادة من المعلومات الديناميكية في وقت الطلب.

لا يلزم أي تكوين إضافي. راجع النشر لمزيد من التفاصيل.

8.2 تصدير ثابت/وضع تطبيق الصفحة الواحدة (SPA/Static Export)

يدعم Next.js أيضًا إخراج موقعك بالكامل كـ تطبيق صفحة واحدة ثابت (SPA).

يمكنك تمكين هذا عن طريق تعيين:

next.config.ts
import type { NextConfig } from 'next';
 
const nextConfig: NextConfig = {
  output: 'export',
};
 
export default nextConfig;

في وضع التصدير الثابت، سينشئ Next.js HTML وCSS وJS ثابتًا بحتًا. لا يمكنك تشغيل كود جانب الخادم (مثل نقاط نهاية واجهة برمجة التطبيقات). إذا كنت لا تزال بحاجة إلى واجهة برمجة تطبيقات، فسيتعين عليك استضافتها بشكل منفصل (مثل خادم Node.js مستقل).

ملاحظة:

  • يمكن تصدير معالجات مسارات GET بشكل ثابت إذا لم تعتمد على بيانات طلب ديناميكية. تصبح ملفات ثابتة في مجلد out الخاص بك.
  • جميع ميزات الخادم الأخرى (الطلبات الديناميكية، إعادة كتابة ملفات تعريف الارتباط، إلخ) غير مدعومة في تصدير SPA بحت.

9. متى تتخطى إنشاء نقطة نهاية لواجهة برمجة التطبيقات

في بعض الحالات، قد لا تحتاج إلى إنشاء نقطة نهاية لواجهة برمجة التطبيقات منفصلة:

  1. جلب البيانات لمكونات الخادم (Server Components): إذا كنت تجلب البيانات فقط لعرضها في صفحة Next.js الخاصة بك، يمكنك جلبها مباشرة في مكون خادم باستخدام async/await دون الحاجة إلى واجهة برمجة تطبيقات منفصلة.
  2. إجراءات الخادم (Server Actions): لمعالجة نماذج أو تنفيذ عمليات جانبية، يمكنك استخدام إجراءات الخادم بدلاً من نقاط نهاية واجهة برمجة التطبيقات التقليدية.
  3. الوظائف الثابتة البسيطة: إذا كانت الوظيفة لا تتطلب بيانات ديناميكية من الطلب، يمكن تنفيذها كدالة JavaScript عادية في كود العميل.

10. جمع كل شيء معًا

لنقم بإنشاء مثال شامل يجمع بين العديد من المفاهيم التي تعلمناها:

app/api/todos/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { withAuth } from '@/lib/with-auth';
import { db } from '@/lib/db';
 
// GET /api/todos/:id
async function getTodo(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  const todo = await db.todo.findUnique({
    where: { id: params.id },
  });
 
  if (!todo) {
    return NextResponse.json(
      { error: 'Todo not found' },
      { status: 404 }
    );
  }
 
  return NextResponse.json(todo);
}
 
// PATCH /api/todos/:id
async function updateTodo(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  const body = await request.json();
  const updated = await db.todo.update({
    where: { id: params.id },
    data: body,
  });
  return NextResponse.json(updated);
}
 
// DELETE /api/todos/:id
async function deleteTodo(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  await db.todo.delete({
    where: { id: params.id },
  });
  return new Response(null, { status: 204 });
}
 
export const GET = withAuth(getTodo);
export const PATCH = withAuth(updateTodo);
export const DELETE = withAuth(deleteTodo);

يوضح هذا المثال:

  • استخدام الوسيط (Middleware) للمصادقة
  • التعامل مع طرق HTTP متعددة
  • المسارات الديناميكية
  • التعامل مع الأخطاء
  • الاتصال بقاعدة بيانات

الختام

يوفر Next.js طريقة قوية لبناء واجهات برمجة التطبيقات جنبًا إلى جنب مع تطبيقات الويب الخاصة بك. من خلال معالجات المسارات (Route Handlers)، يمكنك:

  • إنشاء نقاط نهاية RESTful API
  • التصرف كطبقة وسيطة للخدمات الخلفية
  • تنفيذ منطق المصادقة المشترك
  • التعامل مع الخطافات الويب (Webhooks) والتكاملات

تذكر أن Next.js يدعم أيضًا طرقًا أخرى لجلب البيانات ومعالجتها مثل مكونات الخادم (Server Components) وإجراءات الخادم (Server Actions). اختر النهج الذي يناسب احتياجاتك المحددة.

الأسئلة الشائعة

ماذا عن إجراءات الخادم (Server Actions)؟

إجراءات الخادم (Server Actions) هي طريقة بديلة لتنفيذ عمليات جانب الخادم في Next.js. إنها مفيدة بشكل خاص لمعالجة نماذج HTML وتنفيذ الطفرات (mutations). بينما تعمل معالجات المسارات (Route Handlers) كنقاط نهاية واجهة برمجة تطبيقات تقليدية، تعمل إجراءات الخادم بشكل أكثر تكاملاً مع مكونات React.

هل يمكنني استخدام TypeScript مع معالجات المسارات (Route Handlers)؟

نعم! تدعم معالجات المسارات (Route Handlers) TypeScript بشكل كامل. يوفر Next.js أنواعًا لـ NextRequest، NextResponse، وغيرها من الأدوات المساعدة. يمكنك أيضًا تحديد أنواع لأجسام الطلبات والاستجابات الخاصة بك.

ما هي أفضل الممارسات للمصادقة (Authentication)؟

تتضمن بعض أفضل الممارسات:

  1. استخدم HTTPS دائمًا
  2. تخزين الرموز المميزة (tokens) بشكل آمن في ملفات تعريف الارتباط HTTP-only
  3. تنفيذ التحقق من الصلاحية (validation) على جانب الخادم دائمًا
  4. استخدام وسيط (middleware) للمصادقة المشتركة
  5. النظر في استخدام حلول مثل NextAuth.js أو Auth.js للتعامل مع المصادقة المعقدة

8.3 نشر واجهات برمجة التطبيقات (APIs) على Vercel

إذا كنت تقوم بنشر تطبيق Next.js الخاص بك على Vercel، لدينا دليل حول نشر واجهات برمجة التطبيقات (APIs). يتضمن ذلك ميزات أخرى من Vercel مثل الحد من المعدل البرمجي (programmatic rate-limiting) من خلال جدار الحماية (Firewall) الخاص بـ Vercel. كما يقدم Vercel أيضًا وظائف مجدولة (Cron Jobs)، والتي غالبًا ما تكون مطلوبة مع منهجيات واجهات برمجة التطبيقات.

9. متى تتخطى إنشاء نقطة نهاية لواجهة برمجة التطبيقات (API endpoint)

مع مكونات الخادم في React (React Server Components) في App Router، يمكنك جلب البيانات مباشرة من الخادم دون الكشف عن نقطة نهاية عامة:

app/users/page.tsx
// (مكون الخادم - Server Component)
export default async function UsersPage() {
  // هذا الـ fetch يعمل على الخادم (لا حاجة لكود من جانب العميل هنا)
  const res = await fetch('https://api.example.com/users');
  const data = await res.json();
 
  return (
    <main>
      <h1>المستخدمون</h1>
      <ul>
        {data.map((user: any) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </main>
  );
}

إذا كانت بياناتك تستخدم فقط داخل تطبيق Next.js الخاص بك، فقد لا تحتاج إلى واجهة برمجة تطبيقات عامة على الإطلاق.

10. تجميع كل شيء معًا

  1. إنشاء مشروع Next.js جديد: npx create-next-app@latest --api.
  2. إضافة معالجات المسارات (Route Handlers) داخل مجلد app/ (مثال: app/api/users/route.ts).
  3. تصدير طرق HTTP (GET, POST, PUT, DELETE, إلخ) في نفس الملف.
  4. استخدام واجهات برمجة التطبيقات القياسية للويب (Web Standard APIs) للتفاعل مع كائن Request وإرجاع Response.
  5. بناء واجهة برمجة تطبيقات عامة إذا كنت بحاجة إلى أن تستهلكها عملاء أخرى، أو لوكيل خدمة خلفية.
  6. جلب مسارات واجهة برمجة التطبيقات الجديدة من العميل (مثال: داخل مكون العميل أو باستخدام fetch('/api/...')).
  7. أو تخطي إنشاء واجهة برمجة تطبيقات تمامًا إذا كان مكون الخادم يمكنه جلب البيانات مباشرة.
  8. إضافة نمط "وسيط مشترك (middleware)" (مثال: withAuth()) للمصادقة أو أي منطق متكرر آخر.
  9. النشر على بيئة تدعم Node.js لميزات الخادم، أو تصدير ثابت إذا كنت بحاجة فقط إلى تطبيق صفحة واحدة ثابت (SPA).

الختام

استخدام App Router و Route Handlers في Next.js يمنحك طريقة مرنة وعصرية لبناء واجهات برمجة التطبيقات التي تتبنى منصة الويب (Web Platform) مباشرة. يمكنك:

  • إنشاء واجهة برمجة تطبيقات عامة كاملة لمشاركتها مع الويب أو الهاتف المحمول أو عملاء طرف ثالث.
  • الوكيل وتخصيص المكالمات إلى الخدمات الخارجية الموجودة.
  • تنفيذ طبقة "وسيط (middleware)" قابلة لإعادة الاستخدام للمصادقة أو التسجيل أو أي منطق متكرر.
  • توجيه الطلبات ديناميكيًا باستخدام هيكل مجلدات القطاع [id].

الأسئلة الشائعة

ماذا عن إجراءات الخادم (Server Actions)؟

يمكنك التفكير في إجراءات الخادم (Server Actions) مثل مسارات واجهة برمجة التطبيقات POST المولدة تلقائيًا والتي يمكن استدعاؤها من العميل.

تم تصميمها لعمليات التغيير، مثل إنشاء أو تحديث أو حذف البيانات. تستدعي إجراء الخادم مثل دالة JavaScript عادية، بدلاً من إجراء fetch صريح إلى مسار واجهة برمجة تطبيقات محدد.

بينما لا يزال هناك طلب شبكة يحدث، لا تحتاج إلى إدارته صراحة. يتم إنشاء مسار URL تلقائيًا وتشفيره، لذا لا يمكنك الوصول يدويًا إلى مسار مثل /api/users في المتصفح.

إذا كنت تخطط لاستخدام إجراءات الخادم و كشف واجهة برمجة تطبيقات عامة، نوصي بنقل المنطق الأساسي إلى طبقة وصول البيانات (Data Access Layer) واستدعاء نفس المنطق من كل من إجراء الخادم ومسار واجهة برمجة التطبيقات.

هل يمكنني استخدام TypeScript مع معالجات المسارات (Route Handlers)؟

نعم، يمكنك استخدام TypeScript مع معالجات المسارات. على سبيل المثال، تحديد أنواع Request و Response في ملف route الخاص بك.

تعلم المزيد عن TypeScript مع Next.js.

ما هي أفضل الممارسات للمصادقة؟

تعلم المزيد في توثيق المصادقة الخاص بنا.