Aller au contenu
Lantorian

Next.js10 min de lecture

Données et cache

Depuis Next.js 16, tout est dynamique par défaut et le cache est explicite. Tu décides, composant par composant, ce qui peut être mis en cache et ce qui doit être frais à chaque requête.

Le modèle Cache Components

Un composant marqué 'use cache' est rendu à l'avance et fait partie de la coquille statique. Un composant qui lit des données de requête (cookies(), headers()) reste dynamique et arrive en flux dans un Suspense. Change les interrupteurs, puis simule une navigation.

  • Catégories
    'use cache'
    cacheLife('days')

    Change rarement : une journée de cache.

  • Liste de produits
    'use cache'
    cacheTag('products')

    Taguée : invalidée dès qu'un produit est modifié.

  • Panier
    await cookies()
    <Suspense>

    Lit le cookie de session : toujours dynamique, sous Suspense.

/fr/shoppage complète
Catégoriesdepuis le cache
Liste de produitsdepuis le cache
Panierrendu à la requête
Les blocs en cache apparaissent instantanément ; le panier, propre à chaque utilisateur, ne peut jamais l'être.

Où charger les données

BesoinOutil
Afficher des données dans une pageServer Component async qui appelle une fonction de queries.ts
Créer, modifier, supprimerServer Action dans actions.ts, validée par Zod
Webhook, endpoint appelé par un tiersRoute Handler app/api/…/route.ts
Temps réel, polling, défilement infiniTanStack Query dans un Client Component, rarement

Pas de cascade de requêtes

Quand plusieurs données sont indépendantes, lance-les en parallèle avec Promise.all. Si une seule est lente, isole-la dans son propre Suspense.

À éviter

page.tsx
// Waterfall: 3 sequential requests (300 + 250 + 400 ms)
const customer = await getCustomer(id);
const invoices = await getInvoices(id);
const stats = await getStats(id);

À faire

page.tsx
// In parallel: as slow as the slowest request (400 ms)
const [customer, invoices, stats] = await Promise.all([
  getCustomer(id),
  getInvoices(id),
  getStats(id),
]);

Mettre en cache

'use cache' s'applique à une fonction ou un composant. cacheLife fixe la durée ('hours', 'days', 'max'), cacheTag donne un nom pour invalider plus tard.

next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  cacheComponents: true,    // explicit caching with 'use cache'
  partialPrefetching: true, // Instant Navigations (16.3)
};

export default nextConfig;

Après une modification

Une mutation doit dire à Next.js quelles données ne sont plus à jour. Choisis l'API selon ce que l'utilisateur attend.

APIQuand
updateTag(tag)Dans une Server Action : l'utilisateur doit voir sa modification immédiatement.
revalidateTag(tag, 'max')Contenu qui peut être légèrement en retard : l'ancien est servi pendant le recalcul.
refresh()Rafraîchir uniquement les données non mises en cache (compteur de notifications).
router.refresh()Côté client, recharger les Server Components de la route courante.
src/features/shop/actions.ts
'use server';

import { refresh, revalidateTag, updateTag } from 'next/cache';

export async function renameProduct(id: number, name: string) {
  await apiFetch(`/v1/products/${id}`, { method: 'PATCH', body: { name } });
  updateTag('products'); // the user sees their change right away
}

export async function importCatalog() {
  await apiFetch('/v1/catalog/import', { method: 'POST' });
  revalidateTag('products', 'max'); // stale-while-revalidate for everyone else
}

export async function markNotificationRead(id: number) {
  await apiFetch(`/v1/notifications/${id}/read`, { method: 'POST' });
  refresh(); // re-render uncached data only (header badge)
}

proxy.ts

Next.js 16 renomme middleware.ts en proxy.ts. On y fait la détection de langue et une vérification optimiste de session. L'autorisation réelle reste dans Laravel.

src/proxy.ts
import { NextResponse, type NextRequest } from 'next/server';
import createIntlMiddleware from 'next-intl/middleware';
import { routing } from './i18n/routing';

const intl = createIntlMiddleware(routing);
const protectedPaths = /^\/(fr|en)\/(dashboard|invoices)/;

export default function proxy(request: NextRequest) {
  // Optimistic check only: real authorization happens in Laravel
  const hasSession = request.cookies.has('laravel_session');
  if (protectedPaths.test(request.nextUrl.pathname) && !hasSession) {
    return NextResponse.redirect(new URL('/fr/login', request.url));
  }
  return intl(request);
}

export const config = { matcher: '/((?!api|_next|_vercel|.*\\..*).*)' };

Lire la langue n'importe où

Nouveau dans Next.js 16.3 : next/root-params donne accès au paramètre [locale] depuis n'importe quel Server Component, même dans un 'use cache'.

src/components/price-tag.tsx
import { locale } from 'next/root-params'; // Next.js 16.3

// Any Server Component, however deep, without prop drilling
export async function PriceTag({ amount }: { amount: number }) {
  const current = await locale();
  return <span>{new Intl.NumberFormat(current, { style: 'currency', currency: 'EUR' }).format(amount)}</span>;
}