مسارات 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!' })
}
معلومة مفيدة:
- مسارات API لا تحدد رؤوس CORS، مما يعني أنها للنطاق نفسه فقط بشكل افتراضي. يمكنك تخصيص هذا السلوك عن طريق تغليف معالج الطلب بـ مساعدات طلب CORS.
- لا يمكن استخدام مسارات API مع التصدير الثابت (static exports). ومع ذلك، يمكن استخدام معالجات المسار (Route Handlers) في App Router.
- ستتأثر مسارات API بـ إعدادات
pageExtensions
فيnext.config.js
.
المعاملات
export default function handler(req: NextApiRequest, res: NextApiResponse) {
// ...
}
req
: نسخة من http.IncomingMessageres
: نسخة من http.ServerResponse
طرق 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
كائنًا قابلاً للتسلسل (serializable object)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، ويجب أن يكون هذا كائنًا قابلاً للتسلسل (serializable object). في تطبيق حقيقي، قد ترغب في إعلام العميل بحالة الطلب بناءً على نتيجة نقطة النهاية المطلوبة.
المثال التالي يرسل استجابة 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 المسارات الديناميكية (dynamic routes)، وتتبع نفس قواعد تسمية الملفات المستخدمة لـ 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 الديناميكية لها الأسبقية على مسارات 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
استجابات البث (Streaming)
بينما يدعم Pages Router استجابات البث مع مسارات API، نوصي باعتماد App Router تدريجيًا واستخدام معالجات المسار (Route Handlers) إذا كنت تستخدم Next.js 14+.
إليك كيف يمكنك بث استجابة من مسار API باستخدام writeHead
:
import { NextApiRequest, NextApiResponse } from 'next'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': "no-store",
})
let i = 0
while (i < 10) {
res.write(`data: ${i}\n\n`)
i++
await new Promise((resolve) => setTimeout(resolve, 1000))
}
res.end()
}