Un CMS pour Astro sans renoncer au statique
Comment laisser un client publier sur un site Astro sans passer en rendu serveur, sans lui donner accès au dépôt, et sans qu'une panne du CMS ne mette le site à terre.
Un site Astro qui sort en statique se déploie sur un CDN, coûte presque rien à héberger et tient une pointe de trafic sans rien faire. Le jour où le client veut changer un tarif, tout se complique : soit on lui ouvre le dépôt Git, soit on passe le site en rendu serveur, soit on reçoit le mail « tu peux me changer ça ? » pour la troisième fois du mois.
Aucune de ces trois options n’est bonne. Voici comment on a tranché.
Pourquoi les solutions habituelles coincent
Git-based (Decap, Tina). L’éditeur commite dans le dépôt. Techniquement élégant, mais le client hérite d’un compte GitHub, d’un flux de travail qu’il ne comprend pas, et la moindre erreur de fusion devient un problème pour lui. Surtout, le contenu et le code partagent le même historique : une restauration de contenu touche au dépôt.
Headless classique (Strapi, Sanity, Prismic). Bon produit, mais le plancher payant se situe entre 10 et 18 $ par mois et par projet. Sur un parc de vingt sites vitrine facturés 40 € de maintenance mensuelle, la marge disparaît. Les locales supplémentaires sont presque toujours un palier tarifaire de plus.
Rendu serveur. On perd exactement ce pour quoi on avait choisi Astro : le site devient une application à surveiller, avec un temps de réponse, un cache à penser et une facture d’hébergement qui suit le trafic.
Ce qu’on fait à la place
Le principe tient en une phrase : le CMS ne sert jamais le site, il sert le build.
- L’éditeur travaille dans une interface web, sur son téléphone s’il le veut.
- Quand il clique sur Publier, l’API fige l’état du contenu dans un fichier JSON immuable : un instantané, poussé sur un CDN.
- L’API déclenche ensuite le build chez l’hébergeur du site (Vercel, Netlify, Cloudflare, GitHub Actions ou un serveur à soi).
- Le build lit l’instantané et sort le site statique, comme d’habitude.
Le contenu voyage donc du CMS vers le site au moment du build, et jamais à la requête d’un visiteur. Le site final n’a aucune dépendance vers le CMS : il ne l’appelle pas, ne le connaît pas, et n’a pas besoin qu’il soit allumé.
La conséquence qui compte
Si le CMS tombe, les sites publiés restent en ligne. Ils sont statiques, ils sont sur un CDN, il n’y a rien à tomber.
Mieux : l’instantané étant sur un CDN et non dans l’API, un build lancé pendant une panne du CMS aboutit quand même. C’est un test qu’on fait volontairement, en éteignant l’API pour vérifier qu’un déploiement passe encore.
C’est la différence entre un CMS dont le site dépend, et un CMS dont le site se sert.
Côté développeur : le schéma reste dans le code
Le modèle de contenu est déclaré dans un fichier TypeScript du projet, pas cliqué dans une interface :
import { defineCms, fields } from '@menestrel/fields';
export default defineCms({
collections: {
services: {
label: { fr: 'Prestations', en: 'Services' },
fields: {
title: fields.text({ required: true, localized: true }),
excerpt: fields.textarea({ localized: true }),
price: fields.number(),
cover: fields.image(),
},
},
},
});
Une commande synchronise ce schéma vers le serveur, qui génère les formulaires d’édition correspondants. Le schéma vit donc dans la revue de code, dans les branches et dans l’historique Git, comme le reste du projet. Ce qui n’y vit pas, c’est le contenu : le texte du client n’a rien à faire dans un dépôt.
Côté rendu, le contenu arrive par un loader Content Layer standard, donc getCollection() et le typage fonctionnent comme avec n’importe quelle source Astro.
Ce que ça ne fait pas
Par honnêteté, les limites du modèle :
- La publication n’est pas instantanée. Il y a un build entre le clic et la mise en ligne, en général une à trois minutes. On l’assume et on l’affiche : l’éditeur voit la progression et sait quand son changement est en ligne.
- Ce n’est pas fait pour du contenu qui change toutes les minutes. Un site d’actualité en flux continu veut du rendu serveur, pas ça.
- Ce n’est pas un constructeur de pages. L’éditeur remplit des champs définis par le développeur, il ne déplace pas des blocs sur une grille.
Ces trois limites sont des choix, pas des retards de développement. Elles sont ce qui permet de garder le site statique, l’hébergement à quelques centimes et le modèle mental simple.
Pour essayer
La documentation détaille l’installation, et le starter met un projet debout en une commande :
npm create menestrel@latest
Le plan gratuit couvre un site complet, deux langues comprises, sans carte bancaire.