Comment créer une route ressource
La route ressource te permet de créer un endpoint API te permettant de renvoyer une réponse au format JSON, PDF, XML, CSV ... À peu près tous les formats imaginables qu'un serveur peut renvoyer. Pour la mettre en place, il suffit de ne pas exporter de composant React par défaut.
Dans React Router 7, une route ressource (resource route) n’affiche pas de composant React, mais renvoie directement une réponse HTTP (JSON, CSV, PDF, XML, etc.). C’est idéal pour proposer une API interne ou générer un fichier à télécharger (sitemap, robots.txt, export de données, etc.).
Tu vas apprendre à :
- Créer une route JSON avec
Response.json. - Générer un CSV et un PDF via
new Response. - Déclarer ces routes dans
app/routes.ts. - Ajouter des liens
<Link reloadDocument>pour forcer le téléchargement.
1. Route JSON avec Response.json
1.1. Créer le fichier de route
1import { getUsers } from '~/users.server';2import type { LoaderFunctionArgs } from 'react-router';34// @callout: La fonction loader renvoie du JSON directement5export async function loader({}: LoaderFunctionArgs) {6// ^? Méthode utilitaire pour JSON et headers par défaut7return Response.json(8{ users: await getUsers() },9{10headers: {11'Cache-Control': 'public, max-age=3600', // ^? Mise en cache 1 heure12},13}14);15}
1.2. Déclarer la route
1import { type RouteConfig, index, route } from '@react-router/dev/routes';23export default [4index('routes/home.tsx'),5// ^? Route JSON à la racine6route('users.json', 'routes/users.json.tsx'),7] satisfies RouteConfig;
2. Export CSV manuellement
2.1. Fichier CSV
1import { getUsers } from '~/users.server';23// @callout: Génère une chaîne CSV à partir d’un tableau4export async function loader() {5const users = await getUsers();6// 1. Entête7let csv = ['id', 'name', 'slug'].join(',') + '\r\n';8// 2. Lignes de données9users.forEach((u) => {10csv += [u.id, u.name, u.slug].join(',') + '\r\n';11});1213return new Response(csv, {14headers: {15'Content-Type': 'text/csv',16'Content-Disposition': `attachment; filename="users.csv"`,17},18});19}
2.2. Déclaration dans routes.ts
1import { type RouteConfig, index, route } from '@react-router/dev/routes';23export default [4index('routes/home.tsx'),5route('users.json', 'routes/users.json.tsx'),6route('users.csv', 'routes/users.csv.tsx'),7] satisfies RouteConfig;
3. Générez un PDF avec PDFKit
3.1. Installer PDFKit
1npm install pdfkit @types/pdfkit
3.2. Route PDF
1import PDFDocument from 'pdfkit';2import { getUsers } from '~/users.server';34// @callout: PDFKit génère un flux de données PDF5export async function loader() {6const users = await getUsers();7const doc = new PDFDocument();8const chunks: Uint8Array[] = [];910doc.on('data', (chunk) => chunks.push(chunk));11doc.fontSize(16).text('Users list', { align: 'center' }).moveDown();1213users.forEach((u) => {14doc.fontSize(12).text(`${u.id}: ${u.name}`).moveDown(0.5);15});1617doc.end();1819// ^? Attendre la fin du flux avant de répondre20return new Promise<Response>((resolve) => {21doc.on('end', () => {22const pdfBuffer = Buffer.concat(chunks);23resolve(24new Response(pdfBuffer, {25headers: {26'Content-Type': 'application/pdf',27'Content-Disposition': `attachment; filename="users.pdf"`,28},29})30);31});32});33}
3.3. Ajouter dans routes.ts
1import { type RouteConfig, index, route } from '@react-router/dev/routes';23export default [4index('routes/home.tsx'),5route('users.json', 'routes/users.json.tsx'),6route('users.csv', 'routes/users.csv.tsx'),7route('users.pdf', 'routes/users.pdf.tsx'),8] satisfies RouteConfig;
4. Lier dans le layout
Pour permettre à l’utilisateur de télécharger ces fichiers, ajoute des liens avec reloadDocument :
1import { Link, href, Outlet, useLoaderData } from 'react-router';2import { getUsers } from '~/users.server';34export default function UsersLayout() {5const users = useLoaderData<typeof loader>().users;6return (7<div>8<nav className="flex gap-2">9<Link to={href('/users.json')} reloadDocument className="btn">10JSON11</Link>12<Link to={href('/users.csv')} reloadDocument className="btn">13CSV14</Link>15<Link to={href('/users.pdf')} reloadDocument className="btn">1617</Link>18</nav>19<Outlet />20</div>21);22}
Pourquoi `reloadDocument` ?
Par défaut <Link> effectue une navigation-client, qui ne re-charge pas
un loader renvoyant un fichier binaire. reloadDocument force une requête
complète sur le serveur.
Points clés
- Une route ressource n’exporte pas de composant par défaut.
- Utilise
Response.json(...)ounew Response(...)pour contrôler le body et les headers. - Déclare ces routes au même niveau que tes pages (
app/routes.ts). <Link reloadDocument>force le téléchargement de l’API/fichier.- Très utile pour exposer un sitemap.xml, robots.txt, ou des API simples.
Exercices rapides
- Crée une route
/sitemap.xmlqui renvoie un plan de site au format XML (éléments<url>). - Gère une route
robots.txtstatique vianew Response(...)et ajoute-la dans le layout root. - Simule une route ressource
/api/avatar.pngqui renvoie une image binaire stockée danspublic/.
Comprendre les concepts fondamentaux
Quelle est la principale différence entre les composants client et serveur dans React ?
Optimisation des performances
Quelle technique est recommandée pour éviter les rendus inutiles dans React ?
Architecture des données
Quel hook permet de gérer les effets de bord dans un composant React ?