Apprenez à créer de puissants points de terminaison d'API avec les Route Handlers de Next.js. Ce guide couvre tout, de la configuration de base aux techniques avancées, avec des exemples pratiques et les meilleures pratiques.
Route Handlers Next.js : Un Guide Complet pour la Création de Points de Terminaison d'API
Next.js a révolutionné la façon dont nous construisons des applications web avec ses fonctionnalités puissantes comme le rendu côté serveur, la génération de sites statiques et, maintenant, les Route Handlers. Les Route Handlers offrent un moyen flexible et efficace de créer des points de terminaison d'API directement au sein de votre application Next.js. Ce guide explore le concept des Route Handlers, leurs avantages et comment les utiliser efficacement pour construire des API robustes.
Que sont les Route Handlers de Next.js ?
Les Route Handlers sont des fonctions définies dans le répertoire app
d'un projet Next.js qui traitent les requêtes HTTP entrantes. Contrairement à l'ancienne approche pages/api
(qui utilise les API Routes), les Route Handlers offrent un moyen plus simple et plus flexible de définir des points de terminaison d'API à côté de vos composants React. Ce sont essentiellement des fonctions sans serveur (serverless) exécutées à la périphérie (edge) ou dans l'environnement serveur de votre choix.
Considérez les Route Handlers comme la logique backend de votre application Next.js, responsable du traitement des requêtes, de l'interaction avec les bases de données et du renvoi des réponses.
Avantages de l'utilisation des Route Handlers
- Colocalisation : Les Route Handlers résident directement à côté de vos composants React dans le répertoire
app
, favorisant une meilleure organisation et maintenabilité du code. - Support TypeScript : Le support TypeScript intégré garantit la sécurité des types et une meilleure expérience de développement.
- Intégration de Middleware : Intégrez facilement des middlewares pour des tâches telles que l'authentification, l'autorisation et la validation des requêtes.
- Support du Streaming : Les Route Handlers peuvent diffuser des données en continu (streaming), vous permettant d'envoyer des réponses de manière incrémentielle, ce qui est bénéfique pour les grands ensembles de données ou les processus de longue durée.
- Edge Functions : Déployez les Route Handlers en tant que Edge Functions pour des réponses à faible latence, plus proches de vos utilisateurs, en tirant parti des CDN mondiaux.
- Conception d'API simplifiée : Les Route Handlers fournissent une API claire et intuitive pour la gestion des requêtes et des réponses.
- Intégration des Server Actions : L'intégration étroite avec les Server Actions permet une communication transparente entre vos composants côté client et la logique côté serveur.
Configurer votre projet Next.js
Avant de plonger dans les Route Handlers, assurez-vous d'avoir un projet Next.js configuré avec le répertoire app
. Si vous démarrez un nouveau projet, utilisez la commande suivante :
npx create-next-app@latest my-nextjs-app
Choisissez le répertoire app
pendant le processus d'installation pour activer le nouveau système de routage.
Créer votre premier Route Handler
Créons un point de terminaison d'API simple qui renvoie une réponse JSON. Créez un nouveau répertoire dans le répertoire app
, par exemple, /app/api/hello
. À l'intérieur de ce répertoire, créez un fichier nommé route.ts
(ou route.js
si vous n'utilisez pas TypeScript).
Voici le code de votre premier Route Handler :
// app/api/hello/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.json({ message: 'Hello from Next.js Route Handlers!' });
}
Explication :
import { NextResponse } from 'next/server';
: Importe l'objetNextResponse
, qui est utilisé pour construire les réponses de l'API.export async function GET(request: Request) { ... }
: Définit une fonction asynchrone qui gère les requêtes GET vers le point de terminaison/api/hello
. Le paramètrerequest
fournit l'accès à l'objet de la requête entrante.return NextResponse.json({ message: 'Hello from Next.js Route Handlers!' });
: Crée une réponse JSON avec un message et la renvoie en utilisantNextResponse.json()
.
Maintenant, vous pouvez accéder à ce point de terminaison en naviguant vers /api/hello
dans votre navigateur ou en utilisant un outil comme curl
ou Postman
.
Gérer les différentes méthodes HTTP
Les Route Handlers prennent en charge diverses méthodes HTTP comme GET, POST, PUT, DELETE, PATCH et OPTIONS. Vous pouvez définir des fonctions distinctes pour chaque méthode dans le même fichier route.ts
.
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
// Logique pour récupérer tous les utilisateurs de la base de données
const users = [{ id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Smith' }]; // Données d'exemple
return NextResponse.json(users);
}
export async function POST(request: Request) {
const data = await request.json(); // Analyser le corps de la requête en JSON
// Logique pour créer un nouvel utilisateur dans la base de données en utilisant 'data'
const newUser = { id: 3, name: data.name, email: data.email }; // Exemple
return NextResponse.json(newUser, { status: 201 }); // Renvoyer le nouvel utilisateur avec un code de statut 201 Created
}
Explication :
- La fonction
GET
récupère une liste d'utilisateurs (simulée ici) et la renvoie sous forme de réponse JSON. - La fonction
POST
analyse le corps de la requête en JSON, crée un nouvel utilisateur (simulé) et renvoie le nouvel utilisateur avec un code de statut 201 Created.
Accéder aux données de la requête
L'objet request
fournit un accès à diverses informations sur la requête entrante, y compris les en-têtes, les paramètres de requête et le corps de la requête.
En-têtes
Vous pouvez accéder aux en-têtes de la requête en utilisant la propriété request.headers
:
export async function GET(request: Request) {
const userAgent = request.headers.get('user-agent');
console.log('User Agent:', userAgent);
return NextResponse.json({ userAgent });
}
Paramètres de requête
Pour accéder aux paramètres de requête, vous pouvez utiliser le constructeur URL
:
export async function GET(request: Request) {
const url = new URL(request.url);
const searchParams = new URLSearchParams(url.search);
const id = searchParams.get('id');
console.log('ID:', id);
return NextResponse.json({ id });
}
Corps de la requête
Pour les requêtes POST, PUT et PATCH, vous pouvez accéder au corps de la requête en utilisant les méthodes request.json()
ou request.text()
, en fonction du type de contenu.
export async function POST(request: Request) {
const data = await request.json();
console.log('Data:', data);
return NextResponse.json({ receivedData: data });
}
Renvoyer des réponses
L'objet NextResponse
est utilisé pour construire les réponses de l'API. Il fournit plusieurs méthodes pour définir les en-têtes, les codes de statut et les corps de réponse.
Réponses JSON
Utilisez la méthode NextResponse.json()
pour renvoyer des réponses JSON :
return NextResponse.json({ message: 'Success!', data: { name: 'John Doe' } }, { status: 200 });
Réponses texte
Utilisez le constructeur new Response()
pour renvoyer des réponses en texte brut :
return new Response('Hello, world!', { status: 200, headers: { 'Content-Type': 'text/plain' } });
Redirections
Utilisez NextResponse.redirect()
pour rediriger les utilisateurs vers une URL différente :
import { redirect } from 'next/navigation';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.redirect(new URL('/new-location', request.url));
}
Définir les en-têtes
Vous pouvez définir des en-têtes personnalisés en utilisant l'option headers
dans NextResponse.json()
ou new Response()
:
return NextResponse.json({ message: 'Success!' }, { status: 200, headers: { 'Cache-Control': 'no-cache' } });
Intégration de Middleware
Le middleware vous permet d'exécuter du code avant qu'une requête ne soit traitée par votre Route Handler. C'est utile pour l'authentification, l'autorisation, la journalisation et d'autres préoccupations transversales.
Pour créer un middleware, créez un fichier nommé middleware.ts
(ou middleware.js
) dans le répertoire app
ou n'importe quel sous-répertoire. Le middleware s'appliquera à toutes les routes de ce répertoire et de ses sous-répertoires.
// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token');
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/protected/:path*'], // Appliquer ce middleware aux chemins commençant par /protected/
};
Explication :
- La fonction
middleware
vérifie la présence d'un jeton d'authentification dans les cookies de la requête. - Si le jeton est manquant, elle redirige l'utilisateur vers la page de connexion.
- Sinon, elle permet à la requête de continuer vers le Route Handler.
- L'objet
config
spécifie que ce middleware ne doit s'appliquer qu'aux routes commençant par/protected/
.
Gestion des erreurs
Une gestion appropriée des erreurs est cruciale pour construire des API robustes. Vous pouvez utiliser des blocs try...catch
pour gérer les exceptions et renvoyer des réponses d'erreur appropriées.
export async function GET(request: Request) {
try {
// Simuler une erreur
throw new Error('Something went wrong!');
} catch (error: any) {
console.error('Error:', error);
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
Explication :
- Le bloc
try...catch
intercepte toutes les exceptions qui se produisent dans le Route Handler. - Dans le bloc
catch
, l'erreur est enregistrée, et une réponse d'erreur est renvoyée avec un code de statut 500 Internal Server Error.
Réponses en streaming
Les Route Handlers prennent en charge les réponses en streaming, ce qui vous permet d'envoyer des données de manière incrémentielle au client. C'est particulièrement utile pour les grands ensembles de données ou les processus de longue durée.
import { Readable } from 'stream';
import { NextResponse } from 'next/server';
async function* generateData() {
for (let i = 0; i < 10; i++) {
await new Promise(resolve => setTimeout(resolve, 500)); // Simuler un délai
yield `Data chunk ${i}\n`;
}
}
export async function GET(request: Request) {
const readableStream = Readable.from(generateData());
return new Response(readableStream, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
}
Explication :
- La fonction
generateData
est un générateur asynchrone qui produit des morceaux de données avec un délai. - La méthode
Readable.from()
crée un flux lisible à partir du générateur. - L'objet
Response
est créé avec le flux lisible comme corps, et l'en-têteContent-Type
est défini surtext/plain
.
Authentification et autorisation
La sécurisation de vos points de terminaison d'API est cruciale. Vous pouvez mettre en œuvre l'authentification et l'autorisation en utilisant un middleware ou directement dans vos Route Handlers.
Authentification
L'authentification vérifie l'identité de l'utilisateur qui effectue la requête. Les méthodes d'authentification courantes incluent :
- JWT (JSON Web Tokens) : Générer un jeton lors d'une connexion réussie et le vérifier lors des requêtes ultérieures.
- Authentification basée sur les sessions : Utiliser des cookies pour stocker les identifiants de session et les vérifier à chaque requête.
- OAuth : Déléguer l'authentification à un fournisseur tiers comme Google ou Facebook.
Voici un exemple d'authentification JWT utilisant un middleware :
// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import jwt from 'jsonwebtoken';
const secret = process.env.JWT_SECRET || 'your-secret-key'; // Remplacez par un secret fort généré aléatoirement
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token')?.value;
if (!token) {
return NextResponse.json({ message: 'Authentication required' }, { status: 401 });
}
try {
jwt.verify(token, secret);
return NextResponse.next();
} catch (error) {
return NextResponse.json({ message: 'Invalid token' }, { status: 401 });
}
}
export const config = {
matcher: ['/api/protected/:path*'],
};
Autorisation
L'autorisation détermine à quelles ressources un utilisateur est autorisé à accéder. Ceci est généralement basé sur des rôles ou des permissions.
Vous pouvez mettre en œuvre l'autorisation dans vos Route Handlers en vérifiant les rôles ou les permissions de l'utilisateur et en renvoyant une erreur s'il n'a pas l'accès.
// app/api/admin/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
// Supposons que vous ayez une fonction pour obtenir le rôle de l'utilisateur à partir du jeton ou de la session
const userRole = await getUserRole(request);
if (userRole !== 'admin') {
return NextResponse.json({ message: 'Unauthorized' }, { status: 403 });
}
// Logique pour récupérer les données de l'administrateur
const adminData = { message: 'Admin data' };
return NextResponse.json(adminData);
}
async function getUserRole(request: Request): Promise {
// Remplacez par votre logique réelle pour extraire le rôle de l'utilisateur de la requête
// Cela pourrait impliquer la vérification d'un jeton JWT ou la vérification d'une session
return 'admin'; // Exemple : rôle codé en dur pour la démonstration
}
Déployer les Route Handlers
Les Route Handlers sont déployés en tant que fonctions sans serveur (serverless) sur le fournisseur d'hébergement de votre choix. Next.js prend en charge diverses plateformes de déploiement, notamment Vercel, Netlify, AWS, et plus encore.
Pour Vercel, le déploiement est aussi simple que de connecter votre dépôt Git à Vercel et de pousser votre code. Vercel détecte automatiquement votre projet Next.js et déploie vos Route Handlers en tant que fonctions sans serveur.
Techniques avancées
Edge Functions
Les Route Handlers peuvent être déployés en tant que Edge Functions, qui sont exécutées à la périphérie d'un CDN, plus près de vos utilisateurs. Cela peut réduire considérablement la latence et améliorer les performances.
Pour déployer un Route Handler en tant que Edge Function, ajoutez le runtime edge
à votre fichier route.ts
:
export const runtime = 'edge';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.json({ message: 'Hello from the Edge!' });
}
Server Actions
Les Server Actions vous permettent d'exécuter du code côté serveur directement depuis vos composants React. Les Route Handlers et les Server Actions fonctionnent parfaitement ensemble, vous permettant de construire des applications complexes avec aisance.
Voici un exemple d'utilisation d'une Server Action pour appeler un Route Handler :
// app/components/MyComponent.tsx
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
async function handleSubmit(data: FormData) {
'use server';
const name = data.get('name');
const email = data.get('email');
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify({ name, email }),
});
if (response.ok) {
router.refresh(); // Rafraîchir la page pour refléter les changements
}
}
export default function MyComponent() {
const router = useRouter();
return (
);
}
Mise en cache
La mise en cache peut améliorer considérablement les performances de vos points de terminaison d'API. Vous pouvez utiliser l'en-tête Cache-Control
pour contrôler la manière dont vos réponses sont mises en cache par les navigateurs et les CDN.
return NextResponse.json({ message: 'Success!' }, { status: 200, headers: { 'Cache-Control': 'public, max-age=3600' } });
Cet exemple définit l'en-tête Cache-Control
sur public, max-age=3600
, ce qui indique aux navigateurs et aux CDN de mettre en cache la réponse pendant une heure.
Meilleures pratiques
- Utilisez TypeScript : Tirez parti de la sécurité des types de TypeScript pour améliorer la qualité du code et prévenir les erreurs.
- Validez les requêtes : Validez les requêtes entrantes pour garantir l'intégrité des données et empêcher les entrées malveillantes.
- Gérez les erreurs avec élégance : Mettez en œuvre une gestion appropriée des erreurs pour fournir des messages d'erreur informatifs aux clients.
- Sécurisez vos points de terminaison : Mettez en œuvre l'authentification et l'autorisation pour protéger vos points de terminaison d'API.
- Utilisez des middlewares : Utilisez des middlewares pour les préoccupations transversales comme l'authentification, la journalisation et la validation des requêtes.
- Mettez les réponses en cache : Utilisez la mise en cache pour améliorer les performances de vos points de terminaison d'API.
- Surveillez vos API : Surveillez vos API pour identifier et résoudre rapidement les problèmes.
- Documentez vos API : Documentez vos API pour les rendre faciles à utiliser pour d'autres développeurs. Envisagez d'utiliser des outils comme Swagger/OpenAPI pour la documentation de l'API.
Exemples concrets
Voici quelques exemples concrets de la manière dont les Route Handlers peuvent être utilisés :
- API E-commerce : Créez des points de terminaison d'API pour gérer les produits, les commandes et les utilisateurs.
- API de réseau social : Créez des points de terminaison d'API pour publier des tweets, suivre des utilisateurs et récupérer des chronologies.
- API de système de gestion de contenu (CMS) : Créez des points de terminaison d'API pour gérer le contenu, les utilisateurs et les paramètres.
- API d'analyse de données : Créez des points de terminaison d'API pour collecter et analyser des données. Par exemple, un Route Handler pourrait recevoir des données de pixels de suivi sur différents sites web et agréger les informations pour le reporting.
Exemple de E-commerce international : Un Route Handler utilisé pour récupérer les prix des produits en fonction du pays de l'utilisateur. Le point de terminaison pourrait utiliser la géolocalisation de la requête (dérivée de l'adresse IP) pour déterminer l'emplacement de l'utilisateur et renvoyer les prix dans la devise appropriée. Cela contribue à une expérience d'achat localisée.
Exemple d'authentification globale : Un Route Handler mettant en œuvre l'authentification multifacteur (MFA) pour les utilisateurs du monde entier. Cela pourrait impliquer l'envoi de codes SMS ou l'utilisation d'applications d'authentification, tout en respectant les réglementations sur la confidentialité et les infrastructures de télécommunication des différentes régions.
Livraison de contenu multilingue : Un Route Handler qui livre le contenu dans la langue préférée de l'utilisateur. Celle-ci peut être déterminée à partir de l'en-tête `Accept-Language` dans la requête. Cet exemple souligne la nécessité d'un encodage UTF-8 correct et du support des langues s'écrivant de droite à gauche, le cas échéant.
Conclusion
Les Route Handlers de Next.js offrent un moyen puissant et flexible de créer des points de terminaison d'API directement au sein de votre application Next.js. En tirant parti des Route Handlers, vous pouvez construire des API robustes avec facilité, colocaliser votre logique backend avec vos composants React et profiter de fonctionnalités comme les middlewares, le streaming et les Edge Functions.
Ce guide complet a couvert tout, de la configuration de base aux techniques avancées. En suivant les meilleures pratiques décrites dans ce guide, vous pouvez construire des API de haute qualité qui sont sécurisées, performantes et maintenables.