Une source unique de vérité pour les chemins des routes dans les applications Next.js
Découvrez comment un module de routage centralisé par saisie remplace les chemins de chaînes de caractères dispersés dans Next.js, grâce à des fonctions d’aide pour les segments dynamiques et les exportations de routes groupées.
La plupart des projets Next.js commencent par saisir directement les chemins dans les composants Link et les appels router.push. Cela est rapide pour les premières pages, mais à mesure que l’application grandit, ces mêmes chaînes de caractères se retrouvent dupliquées dans les barres de navigation, les pieds de page, les redirections et les e-mails, et chaque renommage devient une tâche de recherche et de remplacement. Le problème s’aggrave lorsque des assistants de codage basés sur l’IA génèrent une partie du code, car ils suivent rarement de manière cohérente une seule convention. Déplacer tous les chemins dans un module tapé manuellement résout ce problème.
Quels sont les problèmes des chemins en ligne de texte
Voici un composant de navigation typique où chaque destination est une chaîne de caractères littérale :
// ❌ Problematic approach
import Link from 'next/link'
import { useRouter } from 'next/router'
export default function Navbar() {
const router = useRouter()
const handleNavigation = () => {
router.push('/dashboard/user/profile')
}
return (
<nav>
<Link href="/about">About</Link>
<Link href="/contact">Contact</Link>
<Link href="/blog">Blog</Link>
<Link href="/dashboard/analytics">Analytics</Link>
<button onClick={handleNavigation}>Profile</button>
</nav>
)
}
Rien ne fonctionne mal pour l’instant, mais quatre problèmes apparaissent à mesure que la base de code et l’équipe grandissent.
Les renommages deviennent une quête
Changer /blog en /articles signifie trouver toutes les occurrences dans les composants, le code serveur, les tests et la configuration. En omettre une crée un lien mort.
Les fautes d’orthographe échappent à l’attention
Un chemin comme /dashbord n’est qu’une chaîne de caractères, donc il n’est ni reconnu par le compilateur ni par l’outil de vérification. L’erreur se manifeste généralement par une erreur 404, parfois uniquement en environnement de production.
L’éditeur ne peut pas aider
Puisque les routes sont des chaînes non structurées, il n’y a ni complétion automatique, ni fonction « aller à la définition », ni avertissement lorsque une route n’existe plus.
Les membres de l’équipe divergent
Des personnes différentes, ainsi que des instructions d’IA variées, finissent par écrire la même destination de manières légèrement différentes : avec ou sans barre oblique à la fin, avec une chaîne de requête saisie manuellement, etc.
Centraliser les routes dans un seul module
La solution consiste en un seul fichier de configuration que toutes les parties de l’application importent.
Où se trouve le fichier
Un dossier constants ou config situé à côté de app et components permet de le trouver facilement :
/src
/app
/components
/constants
└── routes.ts
/lib
Définition de l’objet des routes
Le module ci-dessous regroupe les routes par domaine. Les pages statiques sont de simples chaînes de caractères. Les pages dynamiques sont de petites fonctions qui prennent le segment variable et retournent le chemin complet, de sorte que les appelsants n’ont jamais besoin de composer manuellement des URL. Les points d’accès API se trouvent sous une clé imbriquée API afin d’être utilisés dans les appels fetch. L’ajout de as const à la fin permet à TypeScript d’inférer les valeurs littérales exactes et marque l’objet comme uniquement lisible, ce qui permet un complétion automatique précise et empêche toute réaffectation accidentelle.
// constants/routes.ts
export const ROUTES = {
// Public routes
HOME: '/',
ABOUT: '/about',
CONTACT: '/contact',
PRICING: '/pricing',
// Auth routes
LOGIN: '/auth/login',
REGISTER: '/auth/register',
FORGOT_PASSWORD: '/auth/forgot-password',
// Blog routes
BLOG: '/blog',
BLOG_DETAIL: (slug: string) => `/blog/${slug}`,
BLOG_CATEGORY: (category: string) => `/blog/category/${category}`,
// Dashboard routes
DASHBOARD: '/dashboard',
DASHBOARD_ANALYTICS: '/dashboard/analytics',
DASHBOARD_SETTINGS: '/dashboard/settings',
DASHBOARD_PROFILE: '/dashboard/profile',
// Dynamic user routes
USER_PROFILE: (userId: string) => `/user/${userId}`,
USER_POSTS: (userId: string) => `/user/${userId}/posts`,
// API routes (useful for fetch calls)
API: {
USERS: '/api/users',
POSTS: '/api/posts',
AUTH: '/api/auth',
},
} as const
// Export route groups for better organization
export const AUTH_ROUTES = {
LOGIN: ROUTES.LOGIN,
REGISTER: ROUTES.REGISTER,
FORGOT_PASSWORD: ROUTES.FORGOT_PASSWORD,
} as const
export const DASHBOARD_ROUTES = {
HOME: ROUTES.DASHBOARD,
ANALYTICS: ROUTES.DASHBOARD_ANALYTICS,
SETTINGS: ROUTES.DASHBOARD_SETTINGS,
PROFILE: ROUTES.DASHBOARD_PROFILE,
} as const
Les deux exportations supplémentaires, AUTH_ROUTES et DASHBOARD_ROUTES, représentent des vues sur l’objet principal destinées au code qui ne s’intéresse qu’à un domaine spécifique, comme le middleware de protection du tableau de bord. Comme elles font référence à ROUTES plutôt que de répéter des chaînes de caractères, il n’y a toujours qu’une seule définition par chemin.
Une amélioration à considérer : si un slug ou un ID peut contenir des espaces ou des caractères spéciaux, il faut l’encadrer dans encodeURIComponent au sein de l’aide-mémoire afin que chaque appelant obtienne automatiquement une URL sécurisée.
Utilisation des routes dans les composants
Le navbar importe désormais des noms plutôt que des chaînes de caractères. Tant Link que la navigation impérative lisent dans le même module, ce qui évite tout conflit entre les deux :
// components/Navbar.tsx
import Link from 'next/link'
import { useRouter } from 'next/router'
import { ROUTES, DASHBOARD_ROUTES } from '@/constants/routes'
export default function Navbar() {
const router = useRouter()
const handleProfileNavigation = () => {
router.push(DASHBOARD_ROUTES.PROFILE)
}
return (
<nav className="flex space-x-4">
<Link href={ROUTES.ABOUT} className="hover:text-blue-500">
About
</Link>
<Link href={ROUTES.CONTACT} className="hover:text-blue-500">
Contact
</Link>
<Link href={ROUTES.BLOG} className="hover:text-blue-500">
Blog
</Link>
<Link href={DASHBOARD_ROUTES.ANALYTICS} className="hover:text-blue-500">
Analytics
</Link>
<button
onClick={handleProfileNavigation}
className="rounded bg-blue-500 px-4 py-2 text-white"
>
Profile
</button>
</nav>
)
}
Notez que cet exemple importe useRouter depuis next/router, qui fait partie du Pages Router. Dans les projets App Router, importez-le plutôt depuis next/navigation à l’intérieur d’un composant client ; le module des routes fonctionne de la même manière avec l’un ou l’autre.
Création de liens dynamiques
C’est avec les segments dynamiques que ce modèle est le plus utile. La liste ci-dessous crée des liens vers chaque article et sa catégorie en appelant les fonctions d’aide avec les données de l’article :
// components/BlogList.tsx
import Link from 'next/link'
import { ROUTES } from '@/constants/routes'
interface Post {
slug: string
title: string
category: string
}
interface BlogListProps {
posts: Post[]
}
export default function BlogList({ posts }: BlogListProps) {
return (
<div className="space-y-4">
{posts.map((post) => (
<article key={post.slug} className="rounded border p-4">
<h2 className="text-xl font-bold">
<Link href={ROUTES.BLOG_DETAIL(post.slug)} className="hover:text-blue-500">
{post.title}
</Link>
</h2>
<Link
href={ROUTES.BLOG_CATEGORY(post.category)}
className="text-sm text-gray-500 hover:text-blue-500"
>
Category: {post.category}
</Link>
</article>
))}
</div>
)
}
Si la structure URL des articles change un jour, par exemple en ajoutant un segment d’année, vous mettez à jour BLOG_DETAIL une seule fois et tous les appels suivront. TypeScript signalera également toute appelation qui transmet un nombre incorrect d’arguments.
Avantages
- Chaque chemin est défini une seule fois et réutilisé partout.
Limites et alternatives
Un fichier de constantes ne vérifie pas s’une page correspondante existe réellement dans le répertoire app ou pages. Si un dossier est renommé sans mettre à jour le module, les liens continuent de ne pas fonctionner. Next.js propose également une option routes typées qui génère des types à partir du système de fichiers ; consultez la documentation actuelle pour connaître son état et déterminer si elle convient à votre configuration. De nombreuses équipes combinent les deux : des types générés pour assurer la correction, et un module de constantes pour des noms lisible et des outils d’aide.
Lorsque des assistants IA contribuent du code, indiquez-leur dans les instructions de votre projet d’importer depuis le module routes.
En résumé
Considérez les URLs comme faisant partie de l’API de votre application plutôt que comme des chaînes de caractères dispersées. Un module routes typé et centralisé permet de transformer les renommages en une seule modification, de détecter les erreurs dans l’éditeur, et offre aux humains comme aux outils un endroit évident à consulter.