# Remix et WordPress headless : loaders, cache et formulaires

> Construire un front Remix sur WPGraphQL : chargement des données par route, en-têtes de cache HTTP et formulaires sans bibliothèque tierce.

- Auteur : Clément Hadrot
- Publié le : 2023-11-24
- Mis à jour le : 2023-11-24
- Catégorie : Headless &amp; API
- URL : https://wpmoderne.dev.wordpress-developpement.fr/headless/remix-wordpress-headless-loaders-cache-formulaires/

## L’essentiel

- Un loader par route, exécuté côté serveur uniquement
- Le cache se règle avec un simple en-tête HTTP
- Les formulaires postent sans JavaScript grâce aux actions

Après plusieurs projets Next.js pour des clients avec un catalogue éditorial simple, un studio de céramique nous a demandé quelque chose de plus léger : un site rapide, sans complexité inutile côté gestion d'état, où chaque page interroge WordPress au moment où elle est demandée. Remix s'est imposé pour sa philosophie : les données se chargent côté serveur, dans une fonction dédiée à chaque route, sans magie cachée.

Ce choix a simplifié beaucoup de choses qu'on avait l'habitude de bricoler ailleurs, en particulier le cache et les formulaires.

## Le loader : une fonction, une route, une requête

Chaque fichier de route Remix exporte une fonction `loader` exécutée côté serveur avant le rendu. C'est elle qui interroge WPGraphQL et transmet les données au composant :

```
export async function loader({ params }) {
  const data = await requeteWPGraphQL(REQUETE_PIECE, { slug: params.slug });
  if (!data.piece) {
    throw new Response('Introuvable', { status: 404 });
  }
  return json(data.piece);
}
```

Pas de `useEffect`, pas d'état de chargement à gérer manuellement : au moment où le composant s'exécute, la donnée est déjà là, résolue côté serveur.

## Le cache par en-tête HTTP, sans magie

Remix expose une fonction `headers` par route, qui définit les en-têtes de la réponse renvoyée au navigateur ou à un CDN placé devant l'application :

```
export function headers() {
  return {
    'Cache-Control': 'public, max-age=60, stale-while-revalidate=600',
  };
}
```

Un contenu de catalogue de céramique change rarement dans la minute : un court `max-age` combiné à un `stale-while-revalidate` généreux suffit à absorber le pic de trafic d'une mise en avant sur les réseaux sociaux, sans jamais afficher une page cassée pendant la revalidation.

> L'essentiel à retenir : Un loader par route, exécuté côté serveur uniquement ; Le cache se règle avec un simple en-tête HTTP ; Les formulaires postent sans JavaScript grâce aux actions

## Les actions : des formulaires qui écrivent vers WordPress

Une fonction `action`, exportée dans le même fichier que le `loader`, reçoit les soumissions de formulaire côté serveur. Pour un formulaire de contact qui écrit dans WordPress via une mutation GraphQL personnalisée :

```
export async function action({ request }) {
  const donnees = await request.formData();
  const resultat = await requeteWPGraphQL(MUTATION_CONTACT, {
    nom: donnees.get('nom'),
    message: donnees.get('message'),
  });
  if (resultat.erreurs) {
    return json({ erreur: 'Envoi impossible' }, { status: 422 });
  }
  return redirect('/contact/merci');
}
```

Le formulaire HTML lui-même n'a besoin d'aucun gestionnaire JavaScript pour fonctionner : un simple `<Form method="post">` de Remix suffit, et l'expérience reste fonctionnelle même si le JavaScript n'a pas encore fini de se charger côté client.

## Gérer les erreurs de validation proprement

- La fonction `action` renvoie un objet d'erreurs typé, jamais une exception non gérée.
- Le composant de route lit cet objet via `useActionData` pour afficher les messages au bon endroit du formulaire.
- Aucune bibliothèque de gestion de formulaire supplémentaire n'a été nécessaire sur ce projet, la plateforme suffisait.

## Un piège rencontré avec l'authentification des mutations

Les mutations WPGraphQL qui écrivent du contenu exigent une authentification, généralement via un jeton transmis en en-tête. Sur Remix, ce jeton doit rester strictement côté serveur, jamais transmis au client : la fonction `action` s'exécutant déjà côté serveur, il suffit de lire le secret depuis les variables d'environnement du serveur, sans jamais l'exposer dans le bundle client.

> Un projet où chaque route sait exactement ce qu'elle charge et ce qu'elle écrit reste lisible bien après le lancement, contrairement à un état global qui grossit au fil des fonctionnalités ajoutées.

## Ce qu'on retient

Remix impose une discipline qui sert particulièrement bien un projet WordPress headless : un loader qui charge, une action qui écrit, des en-têtes qui cachent. Sur un site à dominante éditoriale sans interactivité complexe, cette simplicité se traduit directement par moins de code à maintenir et par des temps de chargement perçus très courts, sans étape de chargement côté client à gérer.
