Accueil / Articles / Une source unique de vérité pour les chemins des routes dans les applications Next.js

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.

1222 mots

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.
  • Les noms de routes mal orthographiés provoquent une erreur de compilation au lieu de générer des erreurs 404.
  • Changer une URL ne nécessite qu’une modification en une seule ligne.
  • La structure reste organisée même avec l’augmentation du nombre de pages.
  • Les éditeurs offrent une fonction de complétion automatique et une vérification de frappe pour chaque destination.
  • Les mêmes constantes sont utilisées pour les barres de navigation, les pieds de page, les cartes du site et les chemins d’arborescence.
  • Un seul fichier permet d’obtenir un aperçu rapide de toutes les pages de l’application.
  • 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.

    Lectures complémentaires