مسارات API

أمثلة

معلومة مفيدة: إذا كنت تستخدم App Router، يمكنك استخدام مكونات الخادم (Server Components) أو معالجات المسار (Route Handlers) بدلاً من مسارات API.

توفر مسارات API حلاً لبناء واجهة برمجة تطبيقات عامة (public API) باستخدام Next.js.

أي ملف داخل مجلد pages/api يتم تعيينه إلى /api/* وسيتم التعامل معه كنقطة نهاية API بدلاً من صفحة. هذه الملفات تعمل فقط على جانب الخادم ولن تزيد من حجم حزمة جانب العميل.

على سبيل المثال، مسار API التالي يعيد استجابة JSON مع رمز حالة 200:

import type { NextApiRequest, NextApiResponse } from 'next'

type ResponseData = {
  message: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}
export default function handler(req, res) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

معلومة مفيدة:

المعاملات

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  // ...
}

طرق HTTP

للتعامل مع طرق HTTP المختلفة في مسار API، يمكنك استخدام req.method في معالج الطلب، كما يلي:

import type { NextApiRequest, NextApiResponse } from 'next'

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === 'POST') {
    // معالجة طلب POST
  } else {
    // التعامل مع أي طريقة HTTP أخرى
  }
}
export default function handler(req, res) {
  if (req.method === 'POST') {
    // معالجة طلب POST
  } else {
    // التعامل مع أي طريقة HTTP أخرى
  }
}

مساعدات الطلب

توفر مسارات API مساعدات طلب مدمجة تقوم بتحليل الطلب الوارد (req):

  • req.cookies - كائن يحتوي على الكوكيز المرسلة مع الطلب. القيمة الافتراضية {}
  • req.query - كائن يحتوي على سلسلة الاستعلام (query string). القيمة الافتراضية {}
  • req.body - كائن يحتوي على الجسم المحلل حسب content-type، أو null إذا لم يتم إرسال جسم

التكوين المخصص

يمكن لكل مسار API تصدير كائن config لتغيير التكوين الافتراضي، وهو كالتالي:

export const config = {
  api: {
    bodyParser: {
      sizeLimit: '1mb',
    },
  },
  // يحدد المدة القصوى المسموح بها لتنفيذ هذه الوظيفة (بالثواني)
  maxDuration: 5,
}

bodyParser مفعل تلقائيًا. إذا كنت تريد استهلاك الجسم كـ Stream أو مع raw-body، يمكنك تعيين هذا إلى false.

إحدى حالات الاستخدام لتعطيل bodyParsing التلقائي هي السماح لك بالتحقق من الجسم الخام لطلب webhook، على سبيل المثال من GitHub.

export const config = {
  api: {
    bodyParser: false,
  },
}

bodyParser.sizeLimit هو الحد الأقصى للحجم المسموح به للجسم المحلل، بأي تنسيق مدعوم من bytes، كما يلي:

export const config = {
  api: {
    bodyParser: {
      sizeLimit: '500kb',
    },
  },
}

externalResolver هو علم صريح يخبر الخادم أن هذا المسار يتم معالجته بواسطة محلل خارجي مثل express أو connect. تفعيل هذا الخيار يعطل التحذيرات للطلبات غير المحلولة.

export const config = {
  api: {
    externalResolver: true,
  },
}

responseLimit مفعل تلقائيًا، ويظهر تحذيرًا عندما يتجاوز حجم استجابة مسارات API 4MB.

إذا كنت لا تستخدم Next.js في بيئة بدون خادم (serverless)، وتفهم الآثار المترتبة على الأداء لعدم استخدام CDN أو مضيف وسائط مخصص، يمكنك تعيين هذا الحد إلى false.

export const config = {
  api: {
    responseLimit: false,
  },
}

يمكن أن يأخذ responseLimit أيضًا عدد البايتات أو أي تنسيق سلسلة مدعوم من bytes، على سبيل المثال 1000، '500kb' أو '3mb'. ستكون هذه القيمة هي الحد الأقصى لحجم الاستجابة قبل ظهور تحذير. القيمة الافتراضية هي 4MB. (انظر أعلاه)

export const config = {
  api: {
    responseLimit: '8mb',
  },
}

مساعدات الاستجابة

كائن استجابة الخادم (Server Response)، (غالبًا ما يختصر بـ res) يتضمن مجموعة من طرق المساعدة المشابهة لـ Express.js لتحسين تجربة المطور وزيادة سرعة إنشاء نقاط نهاية API جديدة.

تشمل طرق المساعدة:

  • res.status(code) - دالة لتعيين رمز الحالة. يجب أن يكون code رمز حالة HTTP صالحًا
  • res.json(body) - إرسال استجابة JSON. يجب أن يكون body كائنًا قابلاً للتسلسل
  • res.send(body) - إرسال استجابة HTTP. يمكن أن يكون body سلسلة، كائن أو Buffer
  • res.redirect([status,] path) - إعادة التوجيه إلى مسار أو URL محدد. يجب أن يكون status رمز حالة HTTP صالحًا. إذا لم يتم تحديده، فإن status يكون افتراضيًا "307" "إعادة توجيه مؤقتة".
  • res.revalidate(urlPath) - إعادة التحقق من صفحة عند الطلب باستخدام getStaticProps. يجب أن يكون urlPath سلسلة.

تعيين رمز حالة الاستجابة

عند إرسال استجابة إلى العميل، يمكنك تعيين رمز حالة الاستجابة.

يضبط المثال التالي رمز حالة الاستجابة إلى 200 (OK) ويعيد خاصية message بقيمة Hello from Next.js! كاستجابة JSON:

import type { NextApiRequest, NextApiResponse } from 'next'

type ResponseData = {
  message: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}
export default function handler(req, res) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

إرسال استجابة JSON

عند إرسال استجابة إلى العميل، يمكنك إرسال استجابة JSON، ويجب أن يكون هذا كائنًا قابلًا للتسلسل. في تطبيق حقيقي، قد ترغب في إعلام العميل بحالة الطلب بناءً على نتيجة نقطة النهاية المطلوبة.

يرسل المثال التالي استجابة JSON مع رمز الحالة 200 (OK) ونتيجة العملية غير المتزامنة. يتم تضمينها في كتلة try catch للتعامل مع أي أخطاء قد تحدث، مع رمز الحالة المناسب ورسالة الخطأ التي يتم التقاطها وإرسالها إلى العميل:

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    const result = await someAsyncOperation()
    res.status(200).json({ result })
  } catch (err) {
    res.status(500).json({ error: 'failed to load data' })
  }
}
export default async function handler(req, res) {
  try {
    const result = await someAsyncOperation()
    res.status(200).json({ result })
  } catch (err) {
    res.status(500).json({ error: 'failed to load data' })
  }
}

إرسال استجابة HTTP

يعمل إرسال استجابة HTTP بنفس طريقة إرسال استجابة JSON. الفرق الوحيد هو أن جسم الاستجابة يمكن أن يكون سلسلة، كائن أو Buffer.

يرسل المثال التالي استجابة HTTP مع رمز الحالة 200 (OK) ونتيجة العملية غير المتزامنة.

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  try {
    const result = await someAsyncOperation()
    res.status(200).send({ result })
  } catch (err) {
    res.status(500).send({ error: 'failed to fetch data' })
  }
}
export default async function handler(req, res) {
  try {
    const result = await someAsyncOperation()
    res.status(200).send({ result })
  } catch (err) {
    res.status(500).send({ error: 'failed to fetch data' })
  }
}

إعادة التوجيه إلى مسار أو URL محدد

على سبيل المثال، قد ترغب في إعادة توجيه عميلك إلى مسار أو URL محدد بمجرد تقديم النموذج.

يعيد المثال التالي توجيه العميل إلى المسار / إذا تم إرسال النموذج بنجاح:

import type { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const { name, message } = req.body

  try {
    await handleFormInputAsync({ name, message })
    res.redirect(307, '/')
  } catch (err) {
    res.status(500).send({ error: 'Failed to fetch data' })
  }
}
export default async function handler(req, res) {
  const { name, message } = req.body

  try {
    await handleFormInputAsync({ name, message })
    res.redirect(307, '/')
  } catch (err) {
    res.status(500).send({ error: 'failed to fetch data' })
  }
}

إضافة أنواع TypeScript

يمكنك جعل مسارات API أكثر أمانًا من حيث النوع عن طريق استيراد أنواع NextApiRequest و NextApiResponse من next، بالإضافة إلى ذلك، يمكنك أيضًا كتابة بيانات الاستجابة الخاصة بك:

import type { NextApiRequest, NextApiResponse } from 'next'

type ResponseData = {
  message: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

معلومة مفيدة: جسم NextApiRequest هو any لأن العميل قد يتضمن أي حمولة. يجب عليك التحقق من نوع/شكل الجسم في وقت التشغيل قبل استخدامه.

مسارات API الديناميكية

تدعم مسارات API المسارات الديناميكية، وتتبع نفس قواعد تسمية الملفات المستخدمة لـ pages/.

import type { NextApiRequest, NextApiResponse } from 'next'

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const { pid } = req.query
  res.end(`Post: ${pid}`)
}
export default function handler(req, res) {
  const { pid } = req.query
  res.end(`Post: ${pid}`)
}

الآن، سيستجيب الطلب إلى /api/post/abc بالنص: Post: abc.

مسارات API الشاملة

يمكن توسيع مسارات API لالتقاط جميع المسارات عن طريق إضافة ثلاث نقاط (...) داخل الأقواس. على سبيل المثال:

  • pages/api/post/[...slug].js يطابق /api/post/a، ولكن أيضًا /api/post/a/b، /api/post/a/b/c وهكذا.

معلومة مفيدة: يمكنك استخدام أسماء أخرى غير slug، مثل: [...param]

سيتم إرسال المعاملات المطابقة كمعامل استعلام (slug في المثال) إلى الصفحة، وسيكون دائمًا مصفوفة، لذا فإن المسار /api/post/a سيكون له كائن query التالي:

{ "slug": ["a"] }

وفي حالة /api/post/a/b، وأي مسار مطابق آخر، ستتم إضافة معاملات جديدة إلى المصفوفة، كما يلي:

{ "slug": ["a", "b"] }

على سبيل المثال:

import type { NextApiRequest, NextApiResponse } from 'next'

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  const { slug } = req.query
  res.end(`Post: ${slug.join(', ')}`)
}
export default function handler(req, res) {
  const { slug } = req.query
  res.end(`Post: ${slug.join(', ')}`)
}

الآن، سيستجيب الطلب إلى /api/post/a/b/c بالنص: Post: a, b, c.

مسارات API الشاملة الاختيارية

يمكن جعل المسارات الشاملة اختيارية عن طريق تضمين المعامل في أقواس مزدوجة ([[...slug]]).

على سبيل المثال، pages/api/post/[[...slug]].js سيطابق /api/post، /api/post/a، /api/post/a/b، وهكذا.

الفرق الرئيسي بين المسارات الشاملة والمسارات الشاملة الاختيارية هو أنه مع المسارات الاختيارية، يتم أيضًا مطابقة المسار بدون المعامل (/api/post في المثال أعلاه).

كائنات query هي كما يلي:

{ } // GET `/api/post` (كائن فارغ)
{ "slug": ["a"] } // `GET /api/post/a` (مصفوفة ذات عنصر واحد)
{ "slug": ["a", "b"] } // `GET /api/post/a/b` (مصفوفة متعددة العناصر)

محاذير

  • تأخذ مسارات API المحددة مسبقًا الأسبقية على مسارات API الديناميكية، وتأخذ المسارات الديناميكية الأسبقية على مسارات API الشاملة. انظر إلى الأمثلة التالية:
    • pages/api/post/create.js - سيطابق /api/post/create
    • pages/api/post/[pid].js - سيطابق /api/post/1، /api/post/abc، إلخ. ولكن ليس /api/post/create
    • pages/api/post/[...slug].js - سيطابق /api/post/1/2، /api/post/a/b/c، إلخ. ولكن ليس /api/post/create، /api/post/abc

مسارات API على الحافة (Edge)

إذا كنت ترغب في استخدام مسارات API مع Edge Runtime، نوصي بتبني App Router تدريجيًا واستخدام معالجات المسار (Route Handlers) بدلاً من مسارات API.

توقيع دالة معالجات المسار متساوي الشكل (isomorphic)، مما يعني أنه يمكنك استخدام نفس الوظيفة لكل من Edge و Node.js runtimes.