در این مقاله قصد داریم بررسی کنیم که چگونه میتوانیم با استفاده از Next.js بهسادگی یک API بسازیم. مباحثی که در این مقاله به آنها پرداختهایم شامل راهاندازی پروژه، درک App Router و Route Handlerها، مدیریت چندین متد HTTP، پیادهسازی مسیریابی داینامیک، ایجاد منطق middleware با قابلیت استفاده مجدد و تصمیمگیری در مورد زمان راهاندازی یک لایه API اختصاصی میباشد.
با استفاده از دستور زیر میتوانیم یک پروژه جدید Next.js را ایجاد کنیم:
npx create-next-app@latest --api
نکتهای که باید به آن توجه داشته باشیم این است که استفاده از flag
--api
به صورت خودکار یک فایل route.ts
نمونه در پوشه app/
پروژه ما اضافه میکند که نحوه ایجاد یک API endpoint را نشان میدهد.
Pages Router: در نسخههای قدیمیتر Next.js، برای ایجاد API از مسیر
pages/api/*
استفاده میشد. این روش بر پایه Node.js request/response و یک API شبیه به Express کار میکرد.
App Router (روش پیشفرض): از نسخه Next.js 13 به بعد، App Router معرفی شد که کاملاً از استانداردهای Request/Response API در وب پیروی میکند. در این روش، به جای
pages/api/*
میتوانیم فایلهای route.ts
یا route.js
را در هر جایی داخل پوشه app/
قرار دهیم.
چرا باید از App Router استفاده کنیم؟
Route Handlerها در App Router به جای APIهای مخصوص Node.js، از استانداردهای Web Platform Request/Response استفاده میکنند. این تغییر باعث سادگی یادگیری، کاهش پیچیدگی و امکان استفاده مجدد از دانش ما در ابزارهای مختلف میشود.
ما میتوانیم یک API عمومی ایجاد کنیم که توسط وب اپلیکیشن Next.js، یک اپلیکیشن موبایل مستقل یا حتی سرویسهای third-party مصرف شود. برای مثال، میتوانیم از مسیر
/api/users
هم در یک وبسایت React و هم در یک اپلیکیشن موبایل React Native داده دریافت نماییم.
گاهی اوقات نیاز داریم که یک لایه میانی برای مخفی کردن یا یکپارچهسازی چندین microservice خارجی ایجاد کنیم. Route Handlerها در Next.js میتوانند نقش یک proxy را داشته باشند و درخواستها را قبل از ارسال به سرور مقصد، پردازش کنند. به عنوان مثال، میتوانیم درخواستها را رهگیری، احراز هویت، تبدیل دادهها و سپس به یک API دیگر ارسال نماییم.
اگر نیاز به دریافت Webhookها یا Callbackهای خارجی از سرویسهایی مانند Stripe، GitHub، Twilio و غیره داریم، میتوانیم از Route Handlerها استفاده کنیم.
اگر نیاز به مدیریت sessionها، توکن یا هر نوع مکانیزم احراز هویت داریم، میتوانیم در لایه API خود کوکیها را ذخیره کنیم، هدرها را بخوانیم و پاسخ مناسب را ارسال نماییم.
نکتهای که وجود دارد این است، اگر فقط به fetch کردن دادهها در سرور برای اپلیکیشن Next.js خود نیاز داریم و قصد اشتراکگذاری آن را با سایر کلاینتها نداریم، میتوانیم مستقیماً از سرور کامپوننتها برای دریافت دادهها در هنگام رندر استفاده کنیم و نیازی به ایجاد یک لایه API جداگانه نخواهیم داشت.
در App Router (داخل پوشه
app/
)، یک پوشه برای مسیر مورد نظر ایجاد میکنیم و یک فایل route.ts
داخل آن قرار میدهیم.
مثلاً برای ایجاد یک endpoint در مسیر
/api/users
، پوشه و فایل به این شکل خواهد بود:
app └── api └── users └── route.ts
برخلاف روش Pages Router API که فقط یک default export داشت، در App Router میتوانیم چندین تابع export کنیم و متدهای HTTP مختلف را در یک فایل مدیریت نماییم:
// app/api/users/route.ts export async function GET(request: Request) { // For example, fetch data from your DB here const users = [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' } ]; return new Response(JSON.stringify(users), { status: 200, headers: { 'Content-Type': 'application/json' } }); } export async function POST(request: Request) { // Parse the request body const body = await request.json(); const { name } = body; // e.g. Insert new user into your DB const newUser = { id: Date.now(), name }; return new Response(JSON.stringify(newUser), { status: 201, headers: { 'Content-Type': 'application/json' } }); }
اکنون:
GET
به /api/users
لیست کاربران را return میکند.POST
به /api/users
یک کاربر جدید را اضافه مینماید.به صورت پیشفرض، متدهای Route Handler مانند
GET
و POST
یک آبجکت Request استاندارد دریافت میکنند و باید یک آبجکت Response استاندارد را return کنند.
۴-۲- پارامترهای کوئری
// 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'); // e.g. `/api/search?query=hello` return new Response( JSON.stringify({ result: `You searched for: ${query}` }), { headers: { 'Content-Type': 'application/json' }, }, ); }
Next.js دو تابع کمکی
cookies()
و headers()
را ارائه میدهد که برای اشتراکگذاری منطق میان کدهای سمت سرور بسیار مفید هستند.
// app/api/auth/route.ts import { NextRequest } from 'next/server'; import { cookies, headers } from 'next/headers'; export async function GET(request: NextRequest) { // ۱٫ Using 'next/headers' helpers const cookieStore = await cookies(); const token = cookieStore.get('token'); const headersList = await headers(); const referer = headersList.get('referer'); // ۲٫ Using the standard Web APIs const userAgent = request.headers.get('user-agent'); return new Response(JSON.stringify({ token, referer, userAgent }), { headers: { 'Content-Type': 'application/json' }, }); }
علاوه بر این، Next.js دو آبجکت
NextRequest
و NextResponse
را ارائه میدهد که نسخههای پیشرفتهای از Web APIهای بیسیک هستند و امکانات بیشتری ارائه میدهند.
برای ایجاد مسیرهای داینامیک (مثلاً
/api/users/:id
) باید از Dynamic Segmentها در ساختار پوشهها استفاده کنیم. به عنوان مثال:
app └── api └── users └── [id] └── route.ts
کدی که داخل
route.ts
قرار دارد:
// 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; // e.g. Query a database for user with ID `id` return new Response(JSON.stringify({ id, name: `User ${id}` }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); } export async function DELETE( request: NextRequest, { params }: { params: Promise<{ id: string }> }, ) { const id = (await params).id; // e.g. Delete user with ID `id` in DB return new Response(null, { status: 204 }); }
این فایل به آدرسی مثل
/api/users/123
مرتبط است، جایی که مقدار ۱۲۳
به عنوان پارامتر دریافت میشود. مقدار params.id
همان بخش داینامیک مسیر را در اختیار ما قرار میدهد.
یک سناریوی رایج این است که 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', { // Optional: forward some headers, add auth tokens, etc. headers: { Authorization: `Bearer ${process.env.API_TOKEN}` }, }); // Transform or forward the response const data = await response.json(); const transformed = { ...data, source: 'proxied-through-nextjs' }; return new Response(JSON.stringify(transformed), { headers: { 'Content-Type': 'application/json' }, }); }
حالا کلاینتها تنها کافی است
/api/external
را صدا بزنند و Next.js درخواست را مدیریت میکند.
این روش گاهی با عنوان «Backend for Frontend» یا BFF نیز شناخته میشود.
اگر بخواهیم منطق یکسانی (مثلاً احراز هویت، لاگگیری و غیره) را در چندین Route Handler اعمال کنیم، میتوانیم توابع قابل استفاده مجدد بسازیم که Handlerهای ما را wrap کنند:
// 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: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }); } // If authenticated, call the original handler return handler(req, context); }; }
کد Route Handler:
// 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: 'Here be dragons' }), { headers: { 'Content-Type': 'application/json' }, }); } export const GET = withAuth(secretGET);
وقتی Next.js را با
next start
روی یک سرور Node.js اجرا کنیم، میتوانیم از قابلیتهایی مانند Route Handlerها، Server Componentها، Middleware و غیره بهرهمند شویم.
هیچ تنظیم اضافهای لازم نیست. برای جزئیات بیشتر مطالعه مستندات دیپلوی Next.js میتواند مفید باشد.
Next.js امکان ایجاد یک سایت کاملاً استاتیک را نیز فراهم میکند، که در آن کل سایت به عنوان یک Single-Page Application (SPA) ساخته میشود.
برای فعال کردن این حالت، در next.config.js مقدار output را برابر “export” قرار میدهیم:
// next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { output: 'export', }; export default nextConfig;
نکات مهمی که وجود دارد عبارتند از:
اگر برنامه خود را روی Vercel دیپلوی میکنیم، Next.js از امکانات ویژهای مانند Rate-limiting برنامهنویسی شده با استفاده از Vercel Firewall و اجرای Cron Jobs برای پردازشهای زمانبندی شده پشتیبانی میکند. برای جزئیات بیشتر، بررسی راهنمای دیپلوی APIها روی Vercel میتواند مفید باشد.
با استفاده از سرور کامپوننتهای React در App Router، میتوانیم دادهها را مستقیماً روی سرور دریافت کنیم، بدون اینکه API عمومی ایجاد نماییم. به عنوان مثال:
// app/users/page.tsx // (Server Component) export default async function UsersPage() { // This fetch runs on the server (no client-side code needed here) const res = await fetch('https://api.example.com/users'); const data = await res.json(); return ( <main> <h1>Users</h1> <ul> {data.map((user: any) => ( <li key={user.id}>{user.name}</li> ))} </ul> </main> ); }
اگر دادههای ما فقط داخل Next.js استفاده میشود، نیازی به ایجاد API عمومی نداریم.
مراحل نهایی برای ایجاد API در Next.js عبارتند از:
npx create-next-app@latest --api
app/
اضافه میکنیم (مثلاً app/api/users/route.ts
).GET
, POST
, PUT
, DELETE
) را در همان فایل export میکنیم.Request
و Response
استفاده میکنیم.fetch('/api/...')
).withAuth()
) برای احراز هویت یا منطق تکراری میسازیم.استفاده از App Router و Route Handlerها در Next.js به ما یک روش مدرن و انعطافپذیر برای ساخت API میدهد که کاملاً با Web Platform هماهنگ است. با این قابلیتها میتوانیم:
[id]
مدیریت کنیم.۵۰ درصد تخفیف ویژه نوروز فرانت کست تا پایان هفته
کد تخفیف: spr
دیدگاهها: