Gestionnaires de route

Les gestionnaires de route (Route Handlers) vous permettent de créer des gestionnaires de requête personnalisés pour une route donnée en utilisant les API Web Request et Response.

Fichier spécial Route.js

Bon à savoir : Les gestionnaires de route sont uniquement disponibles dans le répertoire app. Ils sont l'équivalent des routes API dans le répertoire pages, ce qui signifie que vous n'avez pas besoin d'utiliser à la fois les routes API et les gestionnaires de route.

Convention

Les gestionnaires de route sont définis dans un fichier route.js|ts à l'intérieur du répertoire app :

export async function GET(request: Request) {}
export async function GET(request) {}

Les gestionnaires de route peuvent être imbriqués n'importe où dans le répertoire app, comme page.js et layout.js. Mais il ne peut pas y avoir de fichier route.js au même niveau de segment de route que page.js.

Méthodes HTTP supportées

Les méthodes HTTP suivantes sont supportées : GET, POST, PUT, PATCH, DELETE, HEAD et OPTIONS. Si une méthode non supportée est appelée, Next.js renverra une réponse 405 Method Not Allowed.

API étendues NextRequest et NextResponse

En plus de supporter les API natives Request et Response, Next.js les étend avec NextRequest et NextResponse pour fournir des aides pratiques pour des cas d'utilisation avancés.

Comportement

Mise en cache

Les gestionnaires de route ne sont pas mis en cache par défaut. Vous pouvez cependant activer la mise en cache pour les méthodes GET. Les autres méthodes HTTP supportées ne sont pas mises en cache. Pour mettre en cache une méthode GET, utilisez une option de configuration de route comme export const dynamic = 'force-static' dans votre fichier de gestionnaire de route.

export const dynamic = 'force-static'

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 const dynamic = 'force-static'

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 })
}

Bon à savoir : Les autres méthodes HTTP supportées ne sont pas mises en cache, même si elles sont placées à côté d'une méthode GET qui est mise en cache, dans le même fichier.

Gestionnaires de route spéciaux

Les gestionnaires de route spéciaux comme sitemap.ts, opengraph-image.tsx, icon.tsx et d'autres fichiers de métadonnées restent statiques par défaut, sauf s'ils utilisent des API dynamiques ou des options de configuration dynamiques.

Résolution de route

Vous pouvez considérer une route comme la primitive de routage la plus basique.

  • Elles ne participent pas aux layouts ou aux navigations côté client comme page.
  • Il ne peut pas y avoir de fichier route.js à la même route que page.js.
PageRouteRésultat
app/page.jsapp/route.jsCross Icon Conflit
app/page.jsapp/api/route.jsCheck Icon Valide
app/[user]/page.jsapp/api/route.jsCheck Icon Valide

Chaque fichier route.js ou page.js prend en charge tous les verbes HTTP pour cette route.

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

// ❌ Conflit
// `app/route.ts`
export async function POST(request: Request) {}
export default function Page() {
  return <h1>Hello, Next.js!</h1>
}

// ❌ Conflit
// `app/route.js`
export async function POST(request) {}

Exemples

Les exemples suivants montrent comment combiner les gestionnaires de route avec d'autres API et fonctionnalités de Next.js.

Révalidation des données en cache

Vous pouvez révalider les données en cache en utilisant la Régénération Statique Incrémentielle (ISR) :

export const revalidate = 60

export async function GET() {
  const data = await fetch('https://api.vercel.app/blog')
  const posts = await data.json()

  return Response.json(posts)
}
export const revalidate = 60

export async function GET() {
  const data = await fetch('https://api.vercel.app/blog')
  const posts = await data.json()

  return Response.json(posts)
}

Cookies

Vous pouvez lire ou définir des cookies avec cookies de next/headers. Cette fonction serveur peut être appelée directement dans un gestionnaire de route ou imbriquée dans une autre fonction.

Alternativement, vous pouvez renvoyer une nouvelle Response en utilisant l'en-tête Set-Cookie.

import { cookies } from 'next/headers'

export async function GET(request: Request) {
  const cookieStore = await 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 = await cookies()
  const token = cookieStore.get('token')

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

Vous pouvez également utiliser les API Web sous-jacentes pour lire les cookies de la requête (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')
}

En-têtes

Vous pouvez lire les en-têtes avec headers de next/headers. Cette fonction serveur peut être appelée directement dans un gestionnaire de route ou imbriquée dans une autre fonction.

Cette instance headers est en lecture seule. Pour définir des en-têtes, vous devez renvoyer une nouvelle Response avec de nouveaux headers.

import { headers } from 'next/headers'

export async function GET(request: Request) {
  const headersList = await 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 = await headers()
  const referer = headersList.get('referer')

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

Vous pouvez également utiliser les API Web sous-jacentes pour lire les en-têtes de la requête (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)
}

Redirections

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/')
}

Segments de route dynamiques

Les gestionnaires de route peuvent utiliser des segments dynamiques pour créer des gestionnaires de requête à partir de données dynamiques.

export async function GET(
  request: Request,
  { params }: { params: Promise<{ slug: string }> }
) {
  const { slug } = await params // 'a', 'b', ou 'c'
}
export async function GET(request, { params }) {
  const { slug } = await params // 'a', 'b', ou 'c'
}
RouteURL exempleparams
app/items/[slug]/route.js/items/aPromise<{ slug: 'a' }>
app/items/[slug]/route.js/items/bPromise<{ slug: 'b' }>
app/items/[slug]/route.js/items/cPromise<{ slug: 'c' }>

Paramètres de requête URL

L'objet requête passé au gestionnaire de route est une instance NextRequest, qui inclut quelques méthodes pratiques supplémentaires, comme celles pour gérer plus facilement les paramètres de requête.

import { type NextRequest } from 'next/server'

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

Streaming

Le streaming est couramment utilisé en combinaison avec les modèles de langage de grande taille (LLM), comme OpenAI, pour du contenu généré par IA. En savoir plus sur le SDK IA.

import { openai } from '@ai-sdk/openai'
import { StreamingTextResponse, streamText } from 'ai'

export async function POST(req: Request) {
  const { messages } = await req.json()
  const result = await streamText({
    model: openai('gpt-4-turbo'),
    messages,
  })

  return new StreamingTextResponse(result.toAIStream())
}
import { openai } from '@ai-sdk/openai'
import { StreamingTextResponse, streamText } from 'ai'

export async function POST(req) {
  const { messages } = await req.json()
  const result = await streamText({
    model: openai('gpt-4-turbo'),
    messages,
  })

  return new StreamingTextResponse(result.toAIStream())
}

Ces abstractions utilisent les API Web pour créer un flux. Vous pouvez également utiliser directement les API Web sous-jacentes.

// 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)
}

Corps de la requête

Vous pouvez lire le corps de la Request en utilisant les méthodes standard des API Web :

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 })
}

Corps de la requête FormData

Vous pouvez lire le FormData en utilisant la fonction 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 })
}

Comme les données formData sont toutes des chaînes de caractères, vous pouvez utiliser zod-form-data pour valider la requête et récupérer les données dans le format que vous préférez (par exemple number).

CORS

Vous pouvez définir les en-têtes CORS pour un gestionnaire de route spécifique en utilisant les méthodes standard des API Web :

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 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',
    },
  })
}

Bon à savoir :

  • Pour ajouter des en-têtes CORS à plusieurs gestionnaires de route, vous pouvez utiliser un Middleware ou le fichier next.config.js.
  • Alternativement, consultez notre exemple CORS.

Webhooks

Vous pouvez utiliser un gestionnaire de route (Route Handler) pour recevoir des webhooks depuis des services tiers :

export async function POST(request: Request) {
  try {
    const text = await request.text()
    // Process the webhook payload
  } 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()
    // Process the webhook payload
  } catch (error) {
    return new Response(`Webhook error: ${error.message}`, {
      status: 400,
    })
  }

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

Contrairement aux routes d'API avec le routeur Pages, vous n'avez pas besoin d'utiliser bodyParser ni aucune configuration supplémentaire.

Réponses non-UI

Vous pouvez utiliser des gestionnaires de route pour retourner du contenu non-UI. Notez que sitemap.xml, robots.txt, icônes d'application et images open graph ont tous un support intégré.

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 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>`)
}

Options de configuration des segments

Les gestionnaires de route utilisent la même configuration de segment de route que les pages et les layouts.

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'

Consultez la référence API pour plus de détails.