Gestionnaires de route (Route Handlers)
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.

Bon à savoir : Les gestionnaires de route sont uniquement disponibles dans le répertoire
app
. Ils sont l'équivalent des routes API dans le répertoirepages
, ce qui signifie que vous n'avez pas besoin d'utiliser les routes API et les gestionnaires de route ensemble.
Convention
Les gestionnaires de route sont définis dans un fichier route.js|ts
à l'intérieur du répertoire app
:
export const dynamic = 'force-dynamic' // par défaut à auto
export async function GET(request: Request) {}
export const dynamic = 'force-dynamic' // par défaut à auto
export async function GET(request) {}
Les gestionnaires de route peuvent être imbriqués dans le répertoire app
, de manière similaire à 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 sont mis en cache par défaut lors de l'utilisation de la méthode GET
avec l'objet 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 })
}
Avertissement TypeScript :
Response.json()
n'est valide qu'à partir de TypeScript 5.2. Si vous utilisez une version inférieure de TypeScript, vous pouvez utiliserNextResponse.json()
pour des réponses typées à la place.
Désactivation de la mise en cache
Vous pouvez désactiver la mise en cache en :
- Utilisant l'objet
Request
avec la méthodeGET
. - Utilisant une autre méthode HTTP.
- Utilisant des fonctions dynamiques comme
cookies
etheaders
. - Spécifiant manuellement le mode dynamique via les options de configuration de segment.
Par exemple :
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 })
}
De même, la méthode POST
entraînera une évaluation dynamique du gestionnaire de route.
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)
}
Bon à savoir : Comme les routes API, les gestionnaires de route peuvent être utilisés pour des cas comme la gestion de soumissions de formulaires. Une nouvelle abstraction pour gérer les formulaires et mutations qui s'intègre profondément avec React est en cours de développement.
Résolution de route
Vous pouvez considérer une route
comme la primitive de routage la plus basique.
- Ils 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 quepage.js
.
Page | Route | Résultat |
---|---|---|
app/page.js | app/route.js | Conflit |
app/page.js | app/api/route.js | Valide |
app/[user]/page.js | app/api/route.js | Valide |
Chaque fichier route.js
ou page.js
prend le contrôle de tous les verbes HTTP pour cette route.
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 l'option next.revalidate
:
export async function GET() {
const res = await fetch('https://data.mongodb-api.com/...', {
next: { revalidate: 60 }, // Révalider toutes les 60 secondes
})
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 }, // Révalider toutes les 60 secondes
})
const data = await res.json()
return Response.json(data)
}
Alternativement, vous pouvez utiliser l'option de configuration de segment revalidate
:
export const revalidate = 60
Fonctions dynamiques
Les gestionnaires de route peuvent être utilisés avec des fonctions dynamiques de Next.js, comme cookies
et headers
.
Cookies
Vous pouvez lire ou définir des cookies avec cookies
depuis 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 retourner une nouvelle Response
en utilisant l'en-tête 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}` },
})
}
Vous pouvez aussi utiliser les API Web sous-jacentes pour lire les cookies depuis 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
depuis 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 retourner une nouvelle Response
avec de nouveaux headers
.
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 },
})
}
Vous pouvez aussi utiliser les API Web sous-jacentes pour lire les en-têtes depuis 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
Nous recommandons de lire la page Définition des routes avant de continuer.
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: { slug: string } }
) {
const slug = params.slug // 'a', 'b', ou 'c'
}
export async function GET(request, { params }) {
const slug = params.slug // 'a', 'b', ou 'c'
}
Route | Exemple d'URL | params |
---|---|---|
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' } |
Paramètres de requête URL
L'objet requête passé au gestionnaire de route est une instance NextRequest
, qui a quelques méthodes pratiques supplémentaires, notamment 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. Apprenez-en plus sur le SDK IA.
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)
}
Ces abstractions utilisent les API Web pour créer un flux. Vous pouvez aussi 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 (Request Body)
Vous pouvez lire le corps de la Request
en utilisant les méthodes standard de l'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 les 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 souhaité (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 de l'API Web :
export const dynamic = 'force-dynamic' // par défaut à 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' // par défaut à 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',
},
})
}
Bon à savoir :
- Pour ajouter des en-têtes CORS à plusieurs gestionnaires de route, vous pouvez utiliser un Middleware ou le fichier
next.config.js
.- Vous pouvez également consulter notre exemple CORS.
Webhooks
Vous pouvez utiliser un gestionnaire de route pour recevoir des webhooks depuis des services tiers :
export async function POST(request: Request) {
try {
const text = await request.text()
// Traiter le payload du webhook
} catch (error) {
return new Response(`Erreur de webhook : ${error.message}`, {
status: 400,
})
}
return new Response('Succès !', {
status: 200,
})
}
export async function POST(request) {
try {
const text = await request.text()
// Traiter le payload du webhook
} catch (error) {
return new Response(`Erreur de webhook : ${error.message}`, {
status: 400,
})
}
return new Response('Succès !', {
status: 200,
})
}
Contrairement aux API Routes avec le Pages Router, vous n'avez pas besoin d'utiliser bodyParser
ou une configuration supplémentaire.
Runtimes Edge et Node.js
Les gestionnaires de route utilisent une API Web isomorphe pour supporter à la fois les runtimes Edge et Node.js de manière transparente, y compris le streaming. Comme les gestionnaires de route utilisent la même configuration de segment de route que les pages et layouts, ils supportent des fonctionnalités attendues comme la régénération statique des gestionnaires de route.
Vous pouvez utiliser l'option de configuration runtime
pour spécifier le runtime :
export const runtime = 'edge' // 'nodejs' est la valeur par défaut
Réponses non-UI
Vous pouvez utiliser les gestionnaires de route pour retourner du contenu non-UI. Notez que sitemap.xml
, robots.txt
, app icons
, et les images open graph ont tous un support intégré.
export const dynamic = 'force-dynamic' // par défaut à 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' // par défaut à 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>`)
}
Options de configuration des segments
Les gestionnaires de route utilisent la même configuration de segment de route que les pages et 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.
Routes d'interception
Utilisez les routes d'interception pour charger une nouvelle route dans la mise en page actuelle tout en masquant l'URL du navigateur, utile pour des motifs de routage avancés comme les modales.
Middleware
Apprenez à utiliser le Middleware pour exécuter du code avant qu'une requête ne soit complétée.