Après avoir migré notre démonstration vers Ibexa Commerce 5, nous avons voulu répondre à une question concrète : peut-on conserver Ibexa comme moteur e-commerce tout en construisant une expérience d’achat indépendante avec Next.js ?
La réponse est oui. Dans ce retour d’expérience, nous allons construire une petite boutique réellement connectée à Ibexa : catalogue, fiches produit, variantes, prix, stock, panier, code promotionnel et passage vers le checkout natif.
Le but n’est pas de remplacer Ibexa. Ibexa reste la source de vérité et calcule les données métier. Next.js devient la couche de présentation.
Ce que signifie « headless »
Dans une application Symfony classique, le contrôleur prépare les données et Twig génère directement le HTML envoyé au navigateur.
Dans notre architecture headless :
Navigateur → Next.js → API REST → Ibexa Commerce 5
Ibexa expose des données structurées en JSON. Next.js les récupère, compose les pages et gère l’expérience utilisateur. Le back-office, les produits, les prix, les stocks, les promotions et les commandes restent dans Ibexa.
Cela donne une séparation claire :
- Ibexa Commerce porte les règles métier ;
- l’API REST constitue le contrat d’échange ;
- Next.js affiche le catalogue et orchestre les interactions du visiteur.
Résultat obtenu
Notre démonstration permet de :
- lire le catalogue
demo_catalogdepuis l’API REST ; - afficher une fiche produit rendue côté serveur par Next.js ;
- sélectionner une variante réelle du T-shirt ;
- récupérer le prix et le stock propres à chaque code produit ;
- créer un panier Ibexa depuis Next.js ;
- modifier les quantités et supprimer une ligne ;
- appliquer le code promotionnel
IBEXA15; - afficher les totaux calculés par Ibexa ;
- transférer la session vers le checkout natif d’Ibexa.
Organisation du projet
Le backend Ibexa et le frontend Next.js restent dans le même dépôt pour faciliter cette formation, mais ce sont bien deux applications distinctes :
commerce-skeleton/
├── config/ et src/ # application Ibexa / Symfony
├── public/ # point d’entrée Ibexa
└── frontend-next/ # application Next.js séparée
├── src/app/ # pages et API Routes
├── src/components/ # composants de la boutique
└── src/lib/ # client REST Ibexa
Next.js communique avec le backend configuré dans son environnement :
IBEXA_ORIGIN=http://127.0.0.1:8003
IBEXA_CATALOG=demo_catalog
Le binaire Next.js et ses dépendances sont locaux au dossier frontend-next. Ils ne modifient ni Node.js ni Symfony dans les autres projets.
1. Lire le catalogue Ibexa côté serveur
Le catalogue est récupéré par getCatalogProducts() dans frontend-next/src/lib/ibexa.ts.
L’API interrogée est :
POST /api/ibexa/v2/product/catalog/catalogs/demo_catalog/products/view
La requête contient une ProductQuery avec une pagination, un filtre de disponibilité et un tri par nom. Next.js exécute cet appel côté serveur et transforme la réponse Ibexa en un modèle plus simple pour l’interface.
Ce rendu serveur, ou SSR, apporte deux avantages immédiats : le navigateur reçoit une page déjà construite et les détails techniques de l’appel REST ne sont pas exposés dans le JavaScript client.
2. Afficher un produit et ses variantes
Une variante Ibexa n’est pas seulement une option visuelle. Chaque combinaison possède son propre code produit, son prix et son stock.
Pour notre T-shirt, la couleur et la taille génèrent six codes distincts. La page dynamique Next.js se trouve sous :
/products/[code]
Elle interroge le produit sélectionné puis, pour un produit de base, la vue des variantes :
GET /api/ibexa/v2/product/catalog/products/{code}
POST /api/ibexa/v2/product/catalog/product_variants/view/{baseCode}
Le changement de variante conduit vers le code exact. Le prix et la disponibilité affichés correspondent donc aux données réelles gérées par Ibexa, et non à une valeur simulée dans Next.js.
3. Pourquoi un jeton CSRF et une session sont nécessaires
Certaines vues REST d’Ibexa utilisent une requête POST, même lorsqu’elles servent à lire une liste. Ibexa protège ces requêtes avec un jeton CSRF lié à la session Symfony.
Nous avons ajouté un petit endpoint applicatif :
GET /storefront-api/csrf-token
Il démarre explicitement la session Symfony puis retourne un jeton destiné à l’identifiant rest. Le client serveur Next.js conserve ensemble :
- le jeton CSRF ;
- le cookie de session
IBX_SESSION_ID…renvoyé par Ibexa.
Les appels protégés envoient ensuite les deux valeurs. Un jeton sans la session correspondante est insuffisant et provoque l’erreur Missing or invalid CSRF token.
4. Le panier : Next.js sert de façade, Ibexa calcule
Le navigateur n’appelle pas directement toutes les routes Ibexa. Il utilise les API Routes internes du frontend :
GET|POST|PATCH|DELETE /api/storefront/cart
POST /api/storefront/cart/promotion
Ces routes jouent le rôle de façade :
- elles restaurent ou créent une session Ibexa ;
- elles transmettent la requête à l’API panier ;
- elles normalisent le résumé retourné ;
- elles stockent les identifiants sensibles dans des cookies
HttpOnly.
La création du panier et l’ajout d’une ligne utilisent notamment :
POST /api/ibexa/v2/cart
POST /api/ibexa/v2/cart/{cartIdentifier}/entry
GET /api/ibexa/v2/cart/{cartIdentifier}/summary
Point important : Next.js ne recalcule pas les montants. Le sous-total, la remise, le total hors taxe et le total TTC proviennent du résumé Ibexa.
5. Appliquer une promotion existante
La démonstration réutilise le code IBEXA15 créé dans Ibexa Commerce. Il est appliqué au panier avec :
POST /api/ibexa/v2/discounts_codes/{cartIdentifier}/apply
Avec quatre T-shirts à 29,90 €, le test a produit les valeurs suivantes :
| Élément | Valeur calculée par Ibexa |
|---|---|
| Sous-total | 119,60 € |
| Remise | 17,92 € |
| Total HT | 101,68 € |
| Total TTC | 122,00 € |
Le frontend affiche le résultat, mais la condition d’éligibilité et le montant de la remise restent entièrement sous le contrôle d’Ibexa Commerce.
6. Un checkout hybride pour avancer sans tout réécrire
Nous n’avons pas réimplémenté immédiatement toutes les étapes du checkout dans Next.js. Le bouton « Commander avec Ibexa » redirige vers :
/checkout/init/{cartIdentifier}
Pour que le backend retrouve le panier, le navigateur doit également posséder le véritable cookie de session Ibexa. En développement local, les deux applications utilisent 127.0.0.1 sur des ports différents ; un cookie est attaché au domaine et peut donc accompagner la redirection vers le port du backend.
Cette approche hybride est pragmatique :
- le catalogue et le panier bénéficient de l’interface Next.js ;
- le tunnel de commande éprouvé d’Ibexa reste opérationnel ;
- une future étape pourra remplacer progressivement le checkout sans bloquer la boutique.
Les problèmes rencontrés
Le jeton CSRF était valide mais refusé
La session n’était pas forcément démarrée lors de la génération du jeton. L’appel explicite à Request::getSession()->start() garantit l’émission du cookie correspondant.
La session du panier était perdue au checkout
Un cookie interne Next.js ne suffit pas au backend. Il a fallu transmettre au navigateur le vrai cookie IBX_SESSION_ID… d’Ibexa afin que la redirection retrouve le même panier.
Le port 3000 était déjà occupé
Nous avons conservé l’autre application intacte et lancé ce frontend sur le port 3001. Un serveur Next.js peut choisir un autre port sans modifier le backend :
npm run dev -- --port 3001
Lancer la démonstration en local
Le backend Ibexa doit d’abord répondre sur http://127.0.0.1:8003. Ensuite :
cd frontend-next
cp .env.example .env.local
npm install
npm run dev -- --port 3001
Ouvrez ensuite :
http://127.0.0.1:3001
Parcours conseillé pour vérifier l’intégration :
- ouvrir le catalogue ;
- choisir le T-shirt ;
- changer de variante et vérifier le code, le prix et le stock ;
- ajouter une variante au panier ;
- passer la quantité à quatre ;
- appliquer
IBEXA15; - comparer les montants affichés avec le résumé Ibexa ;
- cliquer sur le bouton de checkout sans valider une commande réelle.
Vérifications techniques
Le frontend a été contrôlé avec :
cd frontend-next
npm run lint
npm run build
Le parcours REST a également été validé jusqu’à la redirection HTTP vers l’étape de sélection d’adresse. Aucune commande n’a été créée pendant ce test.
Sécurité et production
Cette démonstration locale utilise des cookies avec secure: false parce qu’elle fonctionne en HTTP sur 127.0.0.1. En production, il faut au minimum :
- servir le frontend et le backend en HTTPS ;
- activer l’attribut
Securedes cookies ; - définir précisément les domaines et politiques
SameSite; - ne jamais exposer le jeton CSRF ou la session Ibexa dans du JavaScript accessible au navigateur ;
- appliquer les règles CORS uniquement aux origines nécessaires ;
- prévoir une stratégie de cache et d’invalidation adaptée aux prix et aux stocks.
Ce que nous retenons
Le headless ne consiste pas à déplacer la logique e-commerce dans Next.js. La séparation est réussie lorsque Next.js se concentre sur l’expérience et qu’Ibexa reste responsable des produits, variantes, prix, disponibilités, promotions, paniers et commandes.
Cette première version constitue déjà un vrai socle : catalogue SSR, produit multi-variantes, panier REST, promotion et checkout hybride. Les prochaines étapes naturelles sont l’authentification client, le cache avec invalidation, le SEO avancé et, si le besoin le justifie, un checkout entièrement headless.


