معالجات المسارات (Route Handlers)

تسمح لك معالجات المسارات (Route Handlers) بإنشاء معالجات طلبات مخصصة لمسار معين باستخدام واجهات الويب Request و Response.

ملف Route.js الخاص

معلومة مفيدة: معالجات المسارات متاحة فقط داخل دليل app. وهي تعادل مسارات API داخل دليل pages، مما يعني أنك لست بحاجة لاستخدام مسارات API ومعالجات المسارات معًا.

الاصطلاح

يتم تعريف معالجات المسارات في ملف route.js|ts داخل دليل app:

export const dynamic = 'force-dynamic' // الافتراضي هو auto
export async function GET(request: Request) {}
export const dynamic = 'force-dynamic' // الافتراضي هو auto
export async function GET(request) {}

يمكن تداخل معالجات المسارات داخل دليل app، بشكل مشابه لـ page.js و layout.js. لكن لا يمكن وجود ملف route.js في نفس مستوى مقطع المسار مثل page.js.

طرق HTTP المدعومة

طرق HTTP التالية مدعومة: GET, POST, PUT, PATCH, DELETE, HEAD, و OPTIONS. إذا تم استدعاء طريقة غير مدعومة، سترجع Next.js ردًا بـ 405 Method Not Allowed.

واجهات NextRequest و NextResponse الممتدة

بالإضافة إلى دعم واجهتي Request و Response الأصلية، تمتد Next.js مع NextRequest و NextResponse لتوفير أدوات مساعدة لحالات الاستخدام المتقدمة.

السلوك

التخزين المؤقت

يتم تخزين معالجات المسارات مؤقتًا افتراضيًا عند استخدام طريقة GET مع كائن Response.

export async function GET() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY,
    },
  })
  const data = await res.json()

  return Response.json({ data })
}
export async function GET() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY,
    },
  })
  const data = await res.json()

  return Response.json({ data })
}

تحذير TypeScript: Response.json() صالح فقط من TypeScript 5.2. إذا كنت تستخدم إصدارًا أقدم من TypeScript، يمكنك استخدام NextResponse.json() للردود المكتوبة بدلاً من ذلك.

إلغاء التخزين المؤقت

يمكنك إلغاء التخزين المؤقت عن طريق:

مثال:

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const id = searchParams.get('id')
  const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY!,
    },
  })
  const product = await res.json()

  return Response.json({ product })
}
export async function GET(request) {
  const { searchParams } = new URL(request.url)
  const id = searchParams.get('id')
  const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY,
    },
  })
  const product = await res.json()

  return Response.json({ product })
}

بالمثل، ستؤدي طريقة POST إلى تقييم معالج المسار بشكل ديناميكي.

export async function POST() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY!,
    },
    body: JSON.stringify({ time: new Date().toISOString() }),
  })

  const data = await res.json()

  return Response.json(data)
}
export async function POST() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY,
    },
    body: JSON.stringify({ time: new Date().toISOString() }),
  })

  const data = await res.json()

  return Response.json(data)
}

معلومة مفيدة: مثل مسارات API، يمكن استخدام معالجات المسارات لحالات مثل معالجة إرسال النماذج. يتم العمل على تجريد جديد لتعامل مع النماذج والتحولات يتكامل بعمق مع React.

تحليل المسار

يمكنك اعتبار route كأدنى مستوى من بديهيات التوجيه.

  • لا تشارك في التخطيطات أو التنقلات من جانب العميل مثل page.
  • لا يمكن وجود ملف route.js في نفس المسار مثل page.js.
الصفحةالمسارالنتيجة
app/page.jsapp/route.jsCross Icon تعارض
app/page.jsapp/api/route.jsCheck Icon صالح
app/[user]/page.jsapp/api/route.jsCheck Icon صالح

كل ملف route.js أو page.js يأخذ كل أفعال HTTP لذلك المسار.

app/page.js
export default function Page() {
  return <h1>Hello, Next.js!</h1>
}

// ❌ تعارض
// `app/route.js`
export async function POST(request) {}

أمثلة

توضح الأمثلة التالية كيفية دمج معالجات المسارات مع واجهات وميزات Next.js الأخرى.

إعادة التحقق من البيانات المخزنة مؤقتًا

يمكنك إعادة التحقق من البيانات المخزنة مؤقتًا باستخدام خيار next.revalidate:

export async function GET() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    next: { revalidate: 60 }, // إعادة التحقق كل 60 ثانية
  })
  const data = await res.json()

  return Response.json(data)
}
export async function GET() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    next: { revalidate: 60 }, // إعادة التحقق كل 60 ثانية
  })
  const data = await res.json()

  return Response.json(data)
}

بدلاً من ذلك، يمكنك استخدام خيار تكوين المقطع revalidate:

export const revalidate = 60

الدوال الديناميكية

يمكن استخدام معالجات المسارات مع الدوال الديناميكية من Next.js، مثل cookies و headers.

ملفات تعريف الارتباط (Cookies)

يمكنك قراءة أو تعيين ملفات تعريف الارتباط باستخدام cookies من next/headers. يمكن استدعاء هذه الدالة الخادمية مباشرة في معالج المسار، أو تداخلها داخل دالة أخرى.

بدلاً من ذلك، يمكنك إرجاع Response جديدة باستخدام رأس Set-Cookie.

import { cookies } from 'next/headers'

export async function GET(request: Request) {
  const cookieStore = cookies()
  const token = cookieStore.get('token')

  return new Response('Hello, Next.js!', {
    status: 200,
    headers: { 'Set-Cookie': `token=${token.value}` },
  })
}
import { cookies } from 'next/headers'

export async function GET(request) {
  const cookieStore = cookies()
  const token = cookieStore.get('token')

  return new Response('Hello, Next.js!', {
    status: 200,
    headers: { 'Set-Cookie': `token=${token}` },
  })
}

يمكنك أيضًا استخدام واجهات الويب الأساسية لقراءة ملفات تعريف الارتباط من الطلب (NextRequest):

import { type NextRequest } from 'next/server'

export async function GET(request: NextRequest) {
  const token = request.cookies.get('token')
}
export async function GET(request) {
  const token = request.cookies.get('token')
}

الرؤوس (Headers)

يمكنك قراءة الرؤوس باستخدام headers من next/headers. يمكن استدعاء هذه الدالة الخادمية مباشرة في معالج المسار، أو تداخلها داخل دالة أخرى.

نسخة headers هذه للقراءة فقط. لتعيين الرؤوس، تحتاج إلى إرجاع Response جديدة مع رؤوس جديدة.

import { headers } from 'next/headers'

export async function GET(request: Request) {
  const headersList = headers()
  const referer = headersList.get('referer')

  return new Response('Hello, Next.js!', {
    status: 200,
    headers: { referer: referer },
  })
}
import { headers } from 'next/headers'

export async function GET(request) {
  const headersList = headers()
  const referer = headersList.get('referer')

  return new Response('Hello, Next.js!', {
    status: 200,
    headers: { referer: referer },
  })
}

يمكنك أيضًا استخدام واجهات الويب الأساسية لقراءة الرؤوس من الطلب (NextRequest):

import { type NextRequest } from 'next/server'

export async function GET(request: NextRequest) {
  const requestHeaders = new Headers(request.headers)
}
export async function GET(request) {
  const requestHeaders = new Headers(request.headers)
}

إعادة التوجيه (Redirects)

import { redirect } from 'next/navigation'

export async function GET(request: Request) {
  redirect('https://nextjs.org/')
}
import { redirect } from 'next/navigation'

export async function GET(request) {
  redirect('https://nextjs.org/')
}

مقاطع المسار الديناميكية

نوصي بقراءة صفحة تحديد المسارات قبل المتابعة.

يمكن لمعالجات المسارات استخدام المقاطع الديناميكية لإنشاء معالجات طلبات من بيانات ديناميكية.

export async function GET(
  request: Request,
  { params }: { params: { slug: string } }
) {
  const slug = params.slug // 'a', 'b', أو 'c'
}
export async function GET(request, { params }) {
  const slug = params.slug // 'a', 'b', أو 'c'
}
المسارمثال URLparams
app/items/[slug]/route.js/items/a{ slug: 'a' }
app/items/[slug]/route.js/items/b{ slug: 'b' }
app/items/[slug]/route.js/items/c{ slug: 'c' }

معلمات استعلام URL

كائن الطلب الممرر إلى معالج المسار هو نسخة من NextRequest، والتي تحتوي على بعض الطرق الملائمة الإضافية، بما في ذلك لمعالجة معلمات الاستعلام بسهولة أكبر.

import { type NextRequest } from 'next/server'

export function GET(request: NextRequest) {
  const searchParams = request.nextUrl.searchParams
  const query = searchParams.get('query')
  // query هو "hello" لـ /api/search?query=hello
}
export function GET(request) {
  const searchParams = request.nextUrl.searchParams
  const query = searchParams.get('query')
  // query هو "hello" لـ /api/search?query=hello
}

البث (Streaming)

يستخدم البث عادةً مع نماذج اللغة الكبيرة (LLMs)، مثل OpenAI، للمحتوى المولد بواسطة الذكاء الاصطناعي. تعلم المزيد حول AI SDK.

import OpenAI from 'openai'
import { OpenAIStream, StreamingTextResponse } from 'ai'

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
})

export const runtime = 'edge'

export async function POST(req: Request) {
  const { messages } = await req.json()
  const response = await openai.chat.completions.create({
    model: 'gpt-3.5-turbo',
    stream: true,
    messages,
  })

  const stream = OpenAIStream(response)

  return new StreamingTextResponse(stream)
}
import OpenAI from 'openai'
import { OpenAIStream, StreamingTextResponse } from 'ai'

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
})

export const runtime = 'edge'

export async function POST(req) {
  const { messages } = await req.json()
  const response = await openai.chat.completions.create({
    model: 'gpt-3.5-turbo',
    stream: true,
    messages,
  })

  const stream = OpenAIStream(response)

  return new StreamingTextResponse(stream)
}

تستخدم هذه التجريدات واجهات الويب لإنشاء بث. يمكنك أيضًا استخدام واجهات الويب الأساسية مباشرة.

// https://developer.mozilla.org/docs/Web/API/ReadableStream#convert_async_iterator_to_stream
function iteratorToStream(iterator: any) {
  return new ReadableStream({
    async pull(controller) {
      const { value, done } = await iterator.next()

      if (done) {
        controller.close()
      } else {
        controller.enqueue(value)
      }
    },
  })
}

function sleep(time: number) {
  return new Promise((resolve) => {
    setTimeout(resolve, time)
  })
}

const encoder = new TextEncoder()

async function* makeIterator() {
  yield encoder.encode('<p>One</p>')
  await sleep(200)
  yield encoder.encode('<p>Two</p>')
  await sleep(200)
  yield encoder.encode('<p>Three</p>')
}

export async function GET() {
  const iterator = makeIterator()
  const stream = iteratorToStream(iterator)

  return new Response(stream)
}
// https://developer.mozilla.org/docs/Web/API/ReadableStream#convert_async_iterator_to_stream
function iteratorToStream(iterator) {
  return new ReadableStream({
    async pull(controller) {
      const { value, done } = await iterator.next()

      if (done) {
        controller.close()
      } else {
        controller.enqueue(value)
      }
    },
  })
}

function sleep(time) {
  return new Promise((resolve) => {
    setTimeout(resolve, time)
  })
}

const encoder = new TextEncoder()

async function* makeIterator() {
  yield encoder.encode('<p>One</p>')
  await sleep(200)
  yield encoder.encode('<p>Two</p>')
  await sleep(200)
  yield encoder.encode('<p>Three</p>')
}

export async function GET() {
  const iterator = makeIterator()
  const stream = iteratorToStream(iterator)

  return new Response(stream)
}

جسم الطلب (Request Body)

يمكنك قراءة جسم الطلب (Request) باستخدام طرق واجهة برمجة الويب (Web API) القياسية:

export async function POST(request: Request) {
  const res = await request.json()
  return Response.json({ res })
}
export async function POST(request) {
  const res = await request.json()
  return Response.json({ res })
}

بيانات النموذج (FormData) في الطلب

يمكنك قراءة بيانات النموذج (FormData) باستخدام الدالة request.formData():

export async function POST(request: Request) {
  const formData = await request.formData()
  const name = formData.get('name')
  const email = formData.get('email')
  return Response.json({ name, email })
}
export async function POST(request) {
  const formData = await request.formData()
  const name = formData.get('name')
  const email = formData.get('email')
  return Response.json({ name, email })
}

بما أن جميع بيانات formData هي نصوص، يمكنك استخدام zod-form-data للتحقق من صحة الطلب واسترجاع البيانات بالصيغة التي تفضلها (مثل number).

مشاركة الموارد عبر المصادر (CORS)

يمكنك تعيين رؤوس مشاركة الموارد عبر المصادر (CORS headers) لمعالج مسار (Route Handler) معين باستخدام طرق واجهة برمجة الويب القياسية:

export const dynamic = 'force-dynamic' // defaults to auto

export async function GET(request: Request) {
  return new Response('Hello, Next.js!', {
    status: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    },
  })
}
export const dynamic = 'force-dynamic' // defaults to auto

export async function GET(request) {
  return new Response('Hello, Next.js!', {
    status: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    },
  })
}

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

الويب هوكس (Webhooks)

يمكنك استخدام معالج مسار (Route Handler) لاستقبال ويب هوكس (webhooks) من خدمات الطرف الثالث:

export async function POST(request: Request) {
  try {
    const text = await request.text()
    // معالجة حمولة الويب هوكس
  } catch (error) {
    return new Response(`Webhook error: ${error.message}`, {
      status: 400,
    })
  }

  return new Response('Success!', {
    status: 200,
  })
}
export async function POST(request) {
  try {
    const text = await request.text()
    // معالجة حمولة الويب هوكس
  } catch (error) {
    return new Response(`Webhook error: ${error.message}`, {
      status: 400,
    })
  }

  return new Response('Success!', {
    status: 200,
  })
}

من الجدير بالذكر أنه على عكس مسارات واجهة برمجة التطبيقات (API Routes) مع موجه الصفحات (Pages Router)، لا تحتاج إلى استخدام bodyParser أو أي تكوين إضافي.

بيئات التشغيل Edge و Node.js

تمتلك معالجات المسار (Route Handlers) واجهة برمجة تطبيقات ويب (Web API) متساوية الشكل (isomorphic) لدعم كل من بيئات تشغيل Edge و Node.js بسلاسة، بما في ذلك دعم البث (streaming). بما أن معالجات المسار تستخدم نفس تكوين قطعة المسار (route segment configuration) مثل الصفحات والتخطيطات، فإنها تدعم ميزات طال انتظارها مثل معالجات المسار المعاد توليدها بشكل ثابت (statically regenerated) للأغراض العامة.

يمكنك استخدام خيار تكوين قطعة runtime لتحديد بيئة التشغيل:

export const runtime = 'edge' // القيمة الافتراضية هي 'nodejs'

استجابات غير واجهة المستخدم (Non-UI Responses)

يمكنك استخدام معالجات المسار (Route Handlers) لإرجاع محتوى غير واجهة مستخدم. لاحظ أن sitemap.xml, robots.txt, أيقونات التطبيق (app icons), و صور Open Graph جميعها مدعومة مدمجة.

export const dynamic = 'force-dynamic' // defaults to auto

export async function GET() {
  return new Response(
    `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">

<channel>
  <title>Next.js Documentation</title>
  <link>https://nextjs.org/docs</link>
  <description>The React Framework for the Web</description>
</channel>

</rss>`,
    {
      headers: {
        'Content-Type': 'text/xml',
      },
    }
  )
}
export const dynamic = 'force-dynamic' // defaults to auto

export async function GET() {
  return new Response(`<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">

<channel>
  <title>Next.js Documentation</title>
  <link>https://nextjs.org/docs</link>
  <description>The React Framework for the Web</description>
</channel>

</rss>`)
}

خيارات تكوين القطعة (Segment Config Options)

تستخدم معالجات المسار (Route Handlers) نفس تكوين قطعة المسار (route segment configuration) مثل الصفحات والتخطيطات.

export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'
export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'

راجع مرجع واجهة برمجة التطبيقات (API reference) لمزيد من التفاصيل.