Créer une API REST en Symfony avec API Platform : le tuto complet (et réaliste)

Créer une API REST en Symfony avec API Platform : le tuto complet (et réaliste)

Un extranet RH qui expose des données sensibles. Un SaaS qui ouvre son API à des clients tiers. Une app mobile qui repose sur un backend stable, versionnable, documenté...

👉 En 2025, presque tous les produits web pros ont besoin d’une API REST. 

Mais une API, ce n’est pas juste trois routes en JSON. C’est un contrat. Une interface à maintenir. Une base à faire évoluer sans tout casser.

Et c’est là qu’API Platform entre en jeu : rapide à poser, complet, bien intégré à Symfony.
Mais aussi : verbeux, parfois opaque, et vite source de bugs… si on ne sait pas ce qu’il fait exactement.

Chez Yield, agence de développement web, on conçoit des APIs critiques : back-offices interconnectés, portails clients complexes, outils internes à forte logique métier. On a vu les forces d’API Platform — et les problèmes quand on l’utilise comme une boîte noire.

Dans cet article : un vrai guide. Pas un tuto où tout marche au premier composer req.
Une base propre, testable, versionnable, maintenable. Avec les bonnes conventions, les pièges à éviter, et les patterns qu’on applique pour que l’API tienne — en prod, sur la durée.

Setup — poser une base propre

API Platform s’installe en 2 lignes. Mais si vous partez comme ça, vous exposez une base fragile.

Avant de générer quoi que ce soit, posez une structure claire :

composer create-project symfony/skeleton my-api

composer require api orm security lexik/jwt-authentication-bundle

Ensuite, séparez les rôles :

  • Vos entités Doctrine vont dans src/Domain
  • Vos ressources API (DTOs, config) dans src/Api
  • Et surtout : aucune entité exposée directement

Configurez Doctrine sans auto-mapping global. Vous gardez la main sur ce qui est mappé — et ce qui ne l’est pas.

Activez JWT et posez les bases de votre auth dès maintenant. Même si c’est simplifié, vous évitez d’y revenir en urgence dans 3 sprints.

Enfin : prévoyez une base de test propre (env.test, fixtures, factories). Pas un MySQL local bricolé.

🎯 Objectif ici : une base versionnable, testable, maintenable — pas juste une API qui “répond”.

Exposer une ressource — mais penser produit

Créer une ressource API avec API Platform, c’est rapide. Trop rapide, parfois.

Un #[ApiResource] sur une entité Doctrine, un coup de GET /orders, et ça “marche”.

Sauf que :

  • Vous exposez peut-être toute la base sans contrôle.
  • Vous n’avez aucun input propre pour les créations.
  • Et votre Swagger montre des champs que vous ne vouliez pas montrer.

👉 Ce qu’on pose systématiquement chez Yield :

Une vraie ressource métier

Prenons Order :

  • contient un client, un statut, une liste de lignes ;
  • en lecture : vous voulez tout exposer ;
  • en écriture : certaines infos sont calculées, d’autres saisies ;

Résultat : on découpe proprement.

  • Lecture → un DTO OrderOutput, exposé en GET
  • Écriture → un DTO OrderInput, exposé en POST, avec validation dédiée

Ces deux classes sont déclarées comme ApiResource, chacune avec son normalizationContext et denormalizationContext.
Pas besoin de contrôleur — des providers et processors suffisent dans 90 % des cas.

Des groupes de sérialisation clairs

Sans groups, vous exposez tout. Avec trop de groupes, c’est ingérable.
Gardez ça simple : read, write, et des cas métier (read:client, write:admin, etc.)

Une logique métier hors des entités

Pas de if ($this->status === 'cancelled') dans votre modèle Doctrine.
Utilisez les processors pour encapsuler les règles produit : statut, droits, erreurs.

🚨 Exemple : une API exposait un champ status modifiable en direct → tout le workflow de commande est tombé en prod. Depuis : DTO + processor obligatoire.

Structurer vos DTO sans complexifier

Pas besoin de tout découper façon clean architecture.
Un DTO = un use case métier. Ce qui entre (POST), ce qui sort (GET).
Et si votre domaine est bien pensé, vous pouvez réutiliser vos Value Objects côté API.

Attention au PATCH

Le PATCH part sur du merge par défaut. C’est pratique, mais risqué.
Un champ mal contrôlé, et vous écrasez une donnée calculée ou critique.

👉 Ce qu’on recommande souvent : utiliser PUT explicite, ou un processor qui filtre finement les champs modifiables.

Gérer la validation proprement

Vos contraintes doivent être sur les DTOs, pas dans les entités :
@Assert\NotBlank, @Assert\Choice, @Assert\Email… API Platform les prend en charge nativement.

Et surtout, il renvoie des erreurs 422 lisibles côté front — ce qui évite bien des allers-retours avec les équipes produit.

Une ressource, ce n’est pas une entité. C’est une interface pensée pour les usages réels — pas une photo de la base à un instant T.

Gouverner les cas complexes — auth, logique, subresources

Exposer une ressource simple, c’est fait. Maintenant viennent les vrais enjeux.
Et là, API Platform ne vous mâche plus tout.

Sécuriser les accès

Vous pouvez — et devez — sécuriser chaque opération directement dans la ressource :

[ApiResource(
    operations: [
        new Get(security: "is_granted('ROLE_ADMIN')"),
        new Post(securityPostDenormalize: "is_granted('ROLE_USER')")
    ]
)]

C’est lisible, testable, et ça évite les listeners planqués.

On ajoute vite JWT (lexik/jwt-authentication-bundle), un user provider propre, et un mapping de rôles maîtrisé.
Pas juste ROLE_USER. Dans un vrai produit, vous avez des rôles hiérarchiques, des scopes, des accès par périmètre.

👉 Mettez des Voter métier en place dès la V1. C’est ce qui vous sauve à S+6.

Et côté Swagger : pensez à filtrer les endpoints selon les droits (swagger_context) pour éviter de documenter des opérations invisibles ou interdites.

Implémenter la logique métier proprement

Dès que ça dépasse le CRUD, sortez du mode automatique.
Utilisez les processors pour l’écriture (création, update), les providers pour la lecture filtrée.

Exemples :

  • Créer un Order lié à l’utilisateur connecté → processor
  • Calculer un status à la volée sans le stocker → provider
  • Restreindre certains champs selon les rôles → normalizers + processor

💡 Un TicketProvider bien pensé = requête optimisée, pagination native, sécurité intégrée — sans code illisible.

Et surtout : aucune logique métier dans les entités.

Tester les comportements critiques

API Platform n’écrit pas vos tests. Mais il les attend.
Posez dès le départ :

  • des tests HTTP (WebTestCase) sur les endpoints clés ;
  • des tests unitaires sur vos processors et providers ;
  • un env.test réaliste avec fixtures (ou Foundry).

Vous ne testez pas pour “couvrir”, vous testez pour prévenir les régressions produit.

Gérer les subresources (et savoir quand ne pas en faire)

API Platform permet de chaîner les ressources : /clients/{id}/orders, /tickets/{id}/messages.

Mais plus vous descendez, plus c’est risqué : 

  • Sérialisation qui déraille ;
  • Requêtes Doctrine complexes ;
  • Droits d’accès ingérables.

👉 Dans ces cas-là, une opération custom (avec UriTemplate, DTO dédié, processor explicite) est souvent plus simple à maintenir — et plus lisible côté doc.

Ce qu’on dit souvent chez Yield : API Platform est productif, mais exigeant. Si vous le laissez faire, il décide à votre place. Et ce n’est pas toujours ce que vous vouliez.

Cas concret chez Yield — poser une API scalable

Sur une app RH qu’on a accompagnée, l’API devait gérer beaucoup : connexion via SSO, profils salariés, tickets, messages internes, droits complexes… le tout sur trois frontaux différents.

Symfony + API Platform, c’était le bon choix. Mais pas en mode “tout auto”. On a cadré ce qu’on laissait à API Platform — et ce qu’on reprenait en main.

👍 Ce qu’on a laissé :

  • lecture via providers bien ciblés ;
  • écriture via processors simples ;
  • filtres, pagination, doc OpenAPI générée.

👎 Ce qu’on a repris :

  • sécurité granulaire avec voters métier ;
  • historique via EventStore + processor custom ;
  • endpoints custom pour les cas async (ex. génération de documents RH).

Et surtout : pas une seule entité Doctrine exposée. Tout passe par des DTOs pensés produit, versionnés, testés.

Retour d’XP
“Ce qu’on voit souvent, c’est des projets qui exposent tout en ApiResource, sans règles. Ça marche 3 mois. Après, chaque modif casse un usage.
Sur ce projet, on a cadré dès le départ : DTO systématique, pas de logique métier dans les entités, pas d’opé sans test. Résultat : pas une régression entre la V1 et la V2, et une API qui encaisse les évolutions sans douleur.”

– Antoine, lead dev API chez Yield

À la fin : une API propre, documentée, testée, évolutive. Pas un miroir de la base — une interface pensée pour l’usage réel.

Conclusion — Une API, ce n’est pas une feature. C’est une fondation.

Une API REST, ça ne se “fait pas vite fait”. Ça se pense comme une interface produit.
Et avec API Platform, vous pouvez aller vite — si vous gardez le contrôle.

Ce qu’il faut retenir :

  • API Platform génère, mais ne structure pas pour vous.
  • Ne jamais exposer d’entité Doctrine directement.
  • DTO, processors et sécurité par opération : non négociables.
  • Une API, ça se versionne, ça se teste, ça se documente.

Le gain est réel : vélocité, onboarding, lisibilité. Mais la dette peut l’être aussi, si vous laissez la magie faire à votre place.

Chez Yield, on conçoit des APIs qui tournent en prod, pas juste en Swagger. Des APIs qui durent à S+12, même quand l’équipe tourne.

👉 Besoin de poser une base solide ? Ou d’auditer une API existante avant qu’elle n’explose ? Parlons-en.

Abonnez-vous au blog de Yield Studio

Restez en contact avec Yield Studio et recevez les nouveaux articles de blog dans votre boîte de réception.

Oops! Something went wrong while submitting the form.
Yield Studio traitera vos données conformément à sa politique de confidentialité

Yield Studio recrute les top 1% des meilleurs profils tech, product, design

Yield Studio développe des produits digitaux en un temps record

Simulateur

Bienvenue dans le
simulateur d’estimation

Sélectionnez
vos besoins

Sélectionnez un ou plusieurs choix

Définissez les
fonctionnalités

Sélectionnez un ou plusieurs choix

Dernière
étape !

Renseignez votre adresse mail pour recevoir l’estimation !
Obtenez l’estimation
Précédent
Suivant

Bravo ! Vous avez terminé
l’estimation de votre future app !

Vous recevrez dans votre boite mail l’estimation personnalisé. Une estimation vous offre la possibilité de vous projeter dans un budget, vous permettant ainsi de planifier en toute confiance. Néanmoins, chez Yield, nous adoptons une approche agile, prêts à remettre en question et ajuster nos évaluations en fonction de l'évolution de vos besoins et des spécificités de votre projet.
Retour au site
Oops! Something went wrong while submitting the form.