Table des matières :
- Prérequis, versions, et périmètre (ce que l’Admin API fait — et ne fait pas)
- API Platform v3 dans PrestaShop 9 : ressources, formats, OpenAPI, et pièges de sérialisation
- OAuth2 sur l’API d’administration : flows, scopes, rotation, et menaces réalistes
- Endpoints CQRS : aligner l’API sur la réalité PrestaShop (Command/Query Handlers)
- Implémenter un endpoint API Platform v3 qui délègue à CQRS (DTO + Provider/Processor)
- Sécurité, observabilité, performance : ce qui casse en prod (et comment éviter les classiques)
- Adopter l’Admin API sans casser l’existant : coexistence, CI, et rollback contractuel
L’API d’administration PrestaShop 9 n’est pas un “webservice de plus” : c’est un point d’entrée Symfony, pensé pour piloter le back-office (catalogue, commandes, clients, stocks…) via une API moderne, documentée et typée. Le gain est réel pour l’automatisation et les intégrations (ERP/PIM/WMS), mais l’implémentation n’est pas magique : authentification, stabilité des contrats, perf SQL et gouvernance des droits restent votre problème.
Dans les faits, cette Admin API devient vite un “bus” de décisions métier : import catalogue, synchronisation de stock en temps quasi-réel, création de commandes B2B, ou correction de données (adresses, statuts, remboursements). Plus vous la rendez fiable (contrats, droits, observabilité), plus elle vous évite des scripts FTP/cron fragiles et des accès directs à MySQL.
Prérequis, versions, et périmètre (ce que l’Admin API fait — et ne fait pas)
Côté stack, partez sur PrestaShop 9.x (basé sur Symfony 6.4 LTS) et PHP 8.2+ (8.3 recommandé en 2026 pour perf et sécurité). Les exemples ci-dessous supposent un environnement avec accès SSH, un reverse proxy (Nginx/HAProxy), et un accès DB (lecture des logs/EXPLAIN). Si vous êtes en mutualisé sans observabilité, vous allez voler à l’aveugle : l’API admin amplifie les problèmes existants (N+1, verrous InnoDB, timeouts PHP-FPM).
Quelques prérequis “opérationnels” souvent oubliés, mais déterminants pour une Admin API :
- Horodatage fiable : NTP actif côté OS/containers. Les tokens OAuth, TTL, et signatures (JWT) dépendent de l’heure.
- TLS au bon niveau : terminaison TLS au reverse proxy, et chiffrement end-to-end jusqu’à PHP si vous traversez plusieurs hops (proxy → gateway → app).
- Accès aux logs : au minimum accès aux logs applicatifs Symfony + logs proxy (status, latence, upstream). Sans ça, impossible d’expliquer un pic de 401/429/504.
- Stratégie d’erreurs : distinguer 4xx (contrat, auth, validation) et 5xx (bug/infra). Une API d’admin qui renvoie des 500 “génériques” en cas de validation devient inexploitable.
Périmètre : l’Admin API vise les besoins back-office et l’intégration server-to-server. Pour des usages front “headless”, vous aurez souvent une autre couche (BFF/GraphQL/API Gateway) afin d’éviter d’exposer des primitives d’admin à l’extérieur. Si vous partez sur une architecture headless, recadrez le rôle de l’API admin par rapport à votre stratégie (voir Commerce headless : plateforme microservices et API REST/GraphQL scalable).
Concrètement : si votre front React/Next.js appelle directement l’Admin API, vous vous exposez à des risques évidents (scopes trop larges, CORS compliqué, surface d’attaque inutile). À l’inverse, une Admin API derrière un réseau privé (VPN, VPC, IP allowlist) est un excellent “outil de production” pour automatiser.
Enfin, ne confondez pas l’Admin API moderne avec le Webservice historique (XML, clé API) : la coexistence va durer, surtout si vous avez des connecteurs legacy. La migration doit être traitée comme un projet de contrat d’API : versioning, dépréciations, tests de non-régression, et plan de rollback (voir Migration PrestaShop 9 : sécurité, tests et plan de rollback).
Un repère simple pour cadrer la bascule (très utile quand on a un ERP/PIM déjà en place) :
| Besoin d’intégration | Webservice historique | Admin API (API Platform) |
|---|---|---|
| Connecteur existant “legacy” | ✅ souvent | ✅ possible, mais coût de refonte |
| Contrat typé + OpenAPI | ❌ | ✅ |
| Auth moderne (OAuth2, scopes) | ❌ | ✅ |
| Opérations “métier” (CQRS) | ⚠️ bricolage | ✅ naturel |
| Gouvernance (versioning, dépréciations) | ⚠️ | ✅ outillée |
API Platform v3 dans PrestaShop 9 : ressources, formats, OpenAPI, et pièges de sérialisation
PrestaShop 9 s’appuie sur l’écosystème Symfony, et l’Admin API est typiquement construite avec API Platform v3. API Platform standardise trois axes : (1) la définition des ressources (ex. Product, Order) et de leurs opérations (GET/POST/PATCH…), (2) la sérialisation (groups, normalizers), (3) la documentation OpenAPI et la découverte (Swagger UI / ReDoc selon config). En pratique, ça vous donne des endpoints auto-documentés et des schémas exploitables pour générer des clients (TypeScript, Java, etc.).
API Platform peut exposer plusieurs formats (JSON, JSON-LD/Hydra, JSON:API…) selon configuration. En Admin API, gardez une approche “réaliste” : moins de formats = moins de surface de test. Si vous n’avez pas de besoin explicite, un JSON “simple” (et une OpenAPI propre) réduit les ambiguïtés côté intégrateur.
API Platform expose classiquement une doc OpenAPI ; c’est un point de contrôle essentiel, parce qu’il reflète le “contrat” exposé. OpenAPI définit l’objectif sans ambiguïté : « The OpenAPI Specification (OAS) defines a standard, programming language-agnostic interface description for HTTP APIs » (OpenAPI Initiative, spécification OAS). Si votre OpenAPI dérive (changement de type, suppression de champ), vos intégrateurs vont le sentir immédiatement en prod.
Deux pratiques qui évitent 80% des “surprises” de contrat :
- Diff OpenAPI en CI : échouer la pipeline si un endpoint supprime un champ, rend un champ requis, ou change un type sans versioning. Même un contrôle simple (snapshot + diff) fait gagner du temps.
- Exemples de payloads : documenter des exemples (success + erreurs) pour les opérations sensibles (création de commande, mise à jour de stock). C’est souvent plus actionnable que des schémas seuls.
Pour API Platform, la doc officielle est un bon point d’entrée pour les mécanismes (providers/processors, validation, pagination) : https://api-platform.com/docs/core/
Piège classique : la sérialisation “à plat” d’entités riches (Product + déclinaisons + features + images) crée des payloads lourds et des requêtes SQL multiples. Si vous laissez Doctrine sérialiser des proxys à la volée, vous fabriquez du N+1 en API. Il faut raisonner “DTO-first” (projection) et contrôler strictement les champs exposés par serialization groups, sinon vous payez en latence et en charge DB. Pour le diagnostic côté PrestaShop, gardez sous la main un profilage SQL reproductible (voir PrestaShop debug profiling : activer et analyser performances SQL).
Mini-scenario typique : un PIM pousse 5 000 produits mis à jour dans la nuit. Si votre endpoint “upsert product” déclenche des sérialisations profondes (associations, traductions, images), vous cumulez :
- latence par produit (sérialisation + flush),
- verrous MySQL (écritures concurrentes),
- timeouts côté worker (PHP-FPM/queue),
- et surtout : un contrat d’API difficile à stabiliser (car trop proche du modèle interne).
Dans ce cas, la bonne approche est souvent : un endpoint d’écriture minimal (DTO strict) + des endpoints de lecture spécialisés (projections : listing, détail, export), au lieu d’un “gros Product” universel.
OAuth2 sur l’API d’administration : flows, scopes, rotation, et menaces réalistes
L’Admin API PrestaShop 9 est pensée pour être protégée par une authentification robuste (typiquement OAuth 2.0, parfois couplé à OIDC selon les choix d’architecture). Le cadre OAuth n’est pas un détail : « The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service » (RFC 6749, IETF : https://datatracker.ietf.org/doc/html/rfc6749). Traduction opérationnelle : vous déléguez l’accès via des tokens plutôt que de distribuer des mots de passe back-office.
Choix de flow :
- Client Credentials pour les intégrations machine-to-machine (ERP, ETL, jobs CI) : simple, mais nécessite une gouvernance stricte des secrets et de la rotation.
- Authorization Code + PKCE si vous avez une app interactive (admin externe) : vous voulez éviter d’exposer un “client secret” côté navigateur. PKCE est devenu le minimum viable dès qu’il y a un contexte public.
Sur les scopes/permissions, ne faites pas “admin all” par flemme. Un token doit encapsuler un minimum de privilèges (ex. product:read, stock:write). Et surtout : traitez les tokens comme des identifiants temporaires à risque. Mesures concrètes : TTL court (ex. 5–15 min), refresh token avec rotation, révocation, et journalisation. Sur la couche edge, ajoutez du rate limiting et du filtrage IP si possible (voir API Gateway : authentification, rate limiting et routage des microservices et, côté proxy, HAProxy reverse proxy : terminaison TLS, rate limiting et supervision Prometheus).
Pour rendre les scopes actionnables, évitez les scopes “par écran BO” et préférez des scopes “par domaine métier”. Exemple de découpage (à adapter à votre gouvernance) :
| Domaine | Exemples de scopes | Remarques |
|---|---|---|
| Catalogue | catalog:read, catalog:write |
Souvent couplé à un PIM |
| Stock | stock:read, stock:write |
Très sensible (fraude / rupture) |
| Commandes | order:read, order:write |
“write” à restreindre fortement |
| Clients | customer:read |
Attention RGPD (minimisation) |
| Back-office technique | admin:diagnostic |
Exposer peu, logger beaucoup |
Menaces réalistes (celles qui arrivent en prod) :
- Fuite d’un client secret (repo, CI, ticketing) → rotation immédiate, invalidation, et audit des actions.
- Token réutilisé hors contexte (replay) → TTL court + éventuellement contrôle d’audience (
aud) / introspection. - Scopes trop larges → tout fonctionne “jusqu’au jour où” un script supprime ou modifie en masse.
Point conformité (UE/France) : si vous journalisez des actions d’admin (qui, quand, quoi), faites-le de manière proportionnée : pseudonymisation des identifiants quand possible, durée de rétention définie, et séparation entre logs techniques et données métier. C’est un angle souvent négligé quand on active “tout le logging” pour débugger OAuth.
Endpoints CQRS : aligner l’API sur la réalité PrestaShop (Command/Query Handlers)
Si vous avez déjà développé sur le “nouveau” back-office, vous avez croisé le CQRS dans PrestaShop : commandes et requêtes passent par des handlers (CommandBus/QueryBus), souvent avec validation métier explicite. L’idée n’est pas un dogme, c’est une technique pour éviter des contrôleurs obèses et des services “god objects”. Martin Fowler résume l’intérêt sans détour : « At its heart is the notion that you can use a different model to update information than the model you use to read information. » (Martin Fowler, “CQRS” : https://martinfowler.com/bliki/CQRS.html).
Concrètement, côté API, CQRS permet de séparer :
- Query endpoints (lecture) : optimisés pour des projections, pagination, filtres, et caches. C’est le bon endroit pour du
GET /products?updatedSince=.... - Command endpoints (écriture) : centrés sur une intention métier, validation stricte, idempotence (ou au minimum détection de doublons), et gestion claire des erreurs.
Le bénéfice immédiat : vous évitez de mapper naïvement l’API sur vos tables (ps_product, ps_product_lang, etc.). L’API ne doit pas refléter le schéma SQL ; elle doit refléter des cas d’usage. Et quand ça casse, vous avez un point d’entrée unique par command handler pour tracer, mesurer, et corriger (log, métriques, rollback). Pour industrialiser, couplez CQRS avec une observabilité correcte (voir PrestaShop monitoring d’erreurs : logs PHP, MySQL, JavaScript et alertes e-mail).
Deux règles pratiques quand vous exposez des commandes “sensibles” (stock, prix, statuts) :
- Idempotence explicite : acceptez un identifiant de requête (ex. header
Idempotency-Key) ou unexternalReference, et refusez la duplication. C’est indispensable si vos intégrations réessaient en cas de timeout. - Erreurs structurées : renvoyez des erreurs lisibles par machine (code, champ, message), pas uniquement des messages humains. Exemple attendu côté intégrateur : distinguer
validation_error(400) d’uninsufficient_scope(403).
Un modèle d’erreur JSON (simple et efficace) :
{
"error": "validation_error",
"message": "Le champ delta doit être non nul",
"violations": [
{ "field": "delta", "message": "This value should not be null." }
],
"requestId": "01J2ZK9Y7P8E8..."
}
Implémenter un endpoint API Platform v3 qui délègue à CQRS (DTO + Provider/Processor)
Le pattern qui tient la route dans API Platform v3 : exposer un DTO (input/output) et déléguer l’exécution à un State Provider (lecture) ou un State Processor (écriture). Vous gardez les entités internes hors du contrat, vous maîtrisez les champs, et vous réduisez les effets de bord Doctrine. C’est aussi ce qui limite les “breaking changes” quand le cœur PrestaShop bouge.
Exemple minimal (style API Platform v3) : une opération de commande “mise à jour de stock” orientée métier. Attention : le code ci-dessous illustre l’architecture ; adaptez namespaces/services aux conventions exactes de votre projet/module PrestaShop 9.
<?php
// src/Api/Resource/StockAdjustmentResource.php
namespace App\Api\Resource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
#[ApiResource(
operations: [
new Post(
uriTemplate: '/admin/stock/adjustments',
input: StockAdjustmentInput::class,
output: StockAdjustmentOutput::class,
processor: App\Api\State\StockAdjustmentProcessor::class,
),
],
normalizationContext: ['groups' => ['stock:out']],
denormalizationContext: ['groups' => ['stock:in']],
)]
final class StockAdjustmentResource {}
final class StockAdjustmentInput
{
#[Groups(['stock:in'])]
#[Assert\Positive]
public int $productId;
#[Groups(['stock:in'])]
#[Assert\NotNull]
public int $delta;
#[Groups(['stock:in'])]
#[Assert\Length(max: 255)]
public ?string $reason = null;
}
final class StockAdjustmentOutput
{
#[Groups(['stock:out'])]
public string $status;
#[Groups(['stock:out'])]
public int $productId;
#[Groups(['stock:out'])]
public int $newQuantity;
}
Le processor fait le pont vers CQRS (command handler). Vous y appliquez : validation métier, contrôle d’accès (scopes), et idempotence. C’est aussi l’endroit où vous pouvez décider d’une exécution synchrone (retour immédiat) ou asynchrone (message bus + 202 Accepted).
<?php
// src/Api/State/StockAdjustmentProcessor.php
namespace App\Api\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Api\Resource\StockAdjustmentInput;
use App\Api\Resource\StockAdjustmentOutput;
use App\Domain\Stock\Command\AdjustStockCommand;
use App\Domain\Stock\Query\GetStockQuery;
use App\Shared\Bus\CommandBus;
use App\Shared\Bus\QueryBus;
final class StockAdjustmentProcessor implements ProcessorInterface
{
public function __construct(
private CommandBus $commandBus,
private QueryBus $queryBus,
) {}
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): StockAdjustmentOutput
{
/** @var StockAdjustmentInput $data */
$this->commandBus->handle(new AdjustStockCommand(
productId: $data->productId,
delta: $data->delta,
reason: $data->reason,
));
$newQty = $this->queryBus->ask(new GetStockQuery($data->productId));
$out = new StockAdjustmentOutput();
$out->status = 'ok';
$out->productId = $data->productId;
$out->newQuantity = $newQty;
return $out;
}
}
Test rapide côté client (avec token OAuth2 Bearer) :
curl -X POST "https://example.com/api/admin/stock/adjustments" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"productId": 123, "delta": -2, "reason": "inventaire"}'
Sur ce type d’endpoint, un détail “terrain” change tout : la sémantique de delta. Par exemple, un WMS envoie souvent des ajustements par “mouvement” (entrée/sortie), tandis qu’un ERP peut envoyer une “quantité cible” (set). Décidez tôt si votre API expose :
- un endpoint
adjust(delta), idéal pour des mouvements, - et/ou un endpoint
set(quantité cible), idéal pour de la remise à plat après inventaire, sinon vous finirez avec des intégrateurs qui simulent l’un avec l’autre (et des écarts de stock).
Sécurité, observabilité, performance : ce qui casse en prod (et comment éviter les classiques)
Sécurité : l’Admin API est une surface d’attaque back-office. Vous devez traiter ça comme un système critique : TLS strict, durcissement des headers, journalisation, et contrôle d’accès granulaire. Si vous n’avez pas de baseline, partez de mesures concrètes (HSTS, CSP adapté, X-Content-Type-Options, etc.) et automatisez la vérification (voir HTTP Security Headers en PHP : guide CSP, HSTS, X-Frame-Options et Sécurité PrestaShop : protéger API backoffice, WAF et journalisation SIEM).
Quelques “classiques” spécifiques à une API d’admin :
- Protéger la documentation (Swagger/ReDoc) si elle révèle vos routes, schémas et modèles. En interne seulement, ou au minimum derrière auth.
- Désactiver CORS public : une Admin API n’a généralement pas besoin d’être callable depuis le navigateur d’un visiteur.
- Limiter les méthodes et la taille des payloads (proxy + app) : import massif ≠ endpoint public sans garde-fous.
Observabilité : sans corrélation request-id, vous ne débuggerez pas les 401/403/500 intermittents liés à OAuth, aux permissions et aux timeouts DB. Mettez un trace id (header X-Request-ID), logguez systématiquement : subject/token id (pseudonymisé), scope, route, temps total, temps SQL, nombre de requêtes. Pour les erreurs DB et lents, activez un slow query log et traitez-le comme un backlog (voir Requêtes MySQL lentes PrestaShop : activer slow query log et Erreurs base de données OVHcloud : diagnostic et solutions d’hébergement Web).
Un format de log structuré “minimum viable” pour une Admin API (utile même sans stack ELK) :
request_id,route,method,statusauth_subject(hash/pseudo),scopesduration_ms,sql_count,sql_time_mspayload_bytes_in/out
Performance : en API admin, le coût dominant est souvent SQL + sérialisation. Une règle simple : sur une route “listing”, vous devez être capable de tenir un budget du type p95 < 200–300 ms sur un catalogue raisonnable sans exploser le nombre de requêtes. Si vous voyez 200 requêtes SQL sur un GET, c’est un bug d’architecture, pas un “besoin business”. Préparez des projections dédiées, utilisez Redis quand c’est pertinent (cache applicatif, verrous, throttling), et testez sous charge (voir Cache PrestaShop : Varnish, Redis, Memcached et OPcache côté serveur et Redis PrestaShop : configurer le cache sur VPS ou serveur dédié).
Deux garde-fous qui évitent des API “qui marchent en dev mais pas en prod” :
- Pagination obligatoire sur toutes les collections (et plafonds côté serveur). Un
GET /orderssans limite finira en export déguisé. - Filtres indexés : si vous exposez
updatedSince, assurez-vous qu’un index le supporte (sinon chaque sync devient un full scan).
Et côté lecture, pensez “contrats d’export” : un export ERP peut nécessiter des champs différents de l’écran BO. Accepter ce besoin via des endpoints dédiés (ou des paramètres clairement cadrés) évite de gonfler un endpoint générique jusqu’à l’ingérable.
Adopter l’Admin API sans casser l’existant : coexistence, CI, et rollback contractuel
Dans la vraie vie, vous n’éteignez pas l’ancien webservice en une semaine. Organisez une coexistence : endpoints API Platform pour les nouveaux flux, webservice legacy pour les connecteurs non migrés, et une couche d’adaptation si nécessaire. Sur les payloads, versionnez explicitement (URL ou header), documentez les dépréciations et imposez des contract tests basés sur OpenAPI (diff automatique). Sans ça, chaque release devient une roulette russe.
Une manière pragmatique de phaser (souvent efficace en contexte e-commerce FR/EU avec ERP + marketplace) :
- Lecture d’abord : exposez des endpoints de query stables (produits, stocks, commandes) avant d’autoriser des écritures.
- Écritures à faible risque : ex. tags produits, champs custom, ou ajustements de stock sur un périmètre restreint.
- Écritures critiques (prix, commandes, remboursements) seulement quand idempotence + observabilité + rollback sont prêts.
En CI, intégrez des contrôles spécifiques aux endpoints : lint OpenAPI, tests d’auth (scopes), tests de performance basiques (budget de requêtes SQL, timeouts), et SBOM si vous distribuez des modules ou images (voir CI PrestaShop : provenance, SBOM et validation automatique des modules et, pour la compatibilité, Compatibilité modules PrestaShop 9 : checklist avant migration sécurisée).
Enfin, prévoyez un rollback “contractuel” : si un endpoint casse, vous devez pouvoir revenir à la version précédente sans rollback DB destructif. Ça implique des migrations de schéma compatibles (expand/contract), des flags de features, et un plan de restauration testé. Si vous n’avez pas cette discipline, commencez par la mettre en place avant d’exposer des écritures via API (voir Migration PrestaShop 9 : sécurité, tests et plan de rollback).
Un détail utile pour “dé-risquer” la coexistence : faites tourner vos nouveaux consommateurs d’API (jobs ERP/PIM) d’abord en mode audit (lecture + simulation), en enregistrant ce qui serait modifié. Vous validez ainsi :
- le mapping des champs,
- les droits OAuth/scopes,
- les performances,
- et les cas limites (produit inactif, déclinaison manquante, langue absente), sans impacter la prod.
Points de contrôle rapides (à traiter comme une checklist) :
- Auth : OAuth2 avec scopes minimaux, TTL court, rotation des refresh tokens, révocation.
- Contrat : OpenAPI versionné + tests de diff, DTO stables, pas d’exposition directe d’entités.
- CQRS : endpoints d’écriture = intentions métier, handlers traçables, validation métier centralisée.
- Perf : budget de requêtes SQL par endpoint, pagination systématique, pas de N+1, cache ciblé.
- Ops : request-id, logs structurés, alerting, rate limiting edge, runbook d’incident.
