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.
avec React Router 7
Posez vos questions 24/7 à notre IA experte en React Router 7
Validez vos acquis avec des quiz personnalisés et un feedback instantané
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 à :
Response.json
.new Response
.app/routes.ts
.<Link reloadDocument>
pour forcer le téléchargement.Response.json
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}
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;
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}
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;
1npm install pdfkit @types/pdfkit
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}
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;
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}
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.
Response.json(...)
ou new Response(...)
pour contrôler le body et les headers.app/routes.ts
).<Link reloadDocument>
force le téléchargement de l’API/fichier./sitemap.xml
qui renvoie un plan de site au format XML (éléments <url>
).robots.txt
statique via new Response(...)
et ajoute-la dans le layout root./api/avatar.png
qui renvoie une image binaire stockée dans public/
.Quelle est la principale différence entre les composants client et serveur dans React ?
Quelle technique est recommandée pour éviter les rendus inutiles dans React ?
Quel hook permet de gérer les effets de bord dans un composant React ?
Comment implémenter la gestion des erreurs pour les requêtes API dans React ?
Quelle est la meilleure pratique pour déployer une application React en production ?