API Links
Les API Links : Simplifier la Navigation dans vos APIs REST
Introduction
Lorsque vous développez une API REST moderne, l’un des défis majeurs est de fournir aux clients de l’API les liens vers les actions disponibles sur chaque ressource. Faut-il permettre à l’utilisateur de modifier cette ressource ? De la supprimer ? D’effectuer des actions spécifiques ?
Dans notre projet, nous avons développé un système innovant appelé API Links qui résout élégamment ce problème en générant automatiquement les liens d’actions disponibles selon les permissions de l’utilisateur.
Le Problème à Résoudre
Dans une API REST classique, le client doit :
- Connaître toutes les routes disponibles
- Gérer les permissions côté frontend
- Faire des requêtes supplémentaires pour vérifier les droits
Cela crée une forte couplage entre le frontend et le backend, et rend difficile l’évolution de l’API.
Notre Solution : Le Système API Links
Vue d’Ensemble
Le système API Links génère automatiquement les liens vers les actions disponibles pour chaque ressource, en tenant compte des permissions de l’utilisateur connecté. Ces liens sont ajoutés à la réponse JSON-LD sous la propriété hydra:links
.
Architecture du Système
Le système se compose de plusieurs composants clés :
1. Configuration Déclarative (YAML)
Chaque entité peut avoir ses liens définis dans un fichier YAML dans le dossier /config/api_links/
. Voici un exemple pour l’entité Profile
:
App\Entity\Profile:
profile:edit:
route: _api_/v2/startup_requests/{id}{._format}_patch
voterAttribute: !php/const App\Security\Voter\StartupVoter::EDIT
profile:delete:
route: delete_profile
voterAttribute: !php/const App\Security\Voter\StartupVoter::DELETE
benchmarks:list:
route: api_benchmark_results_get_collection
voterAttribute: !php/const App\Security\Voter\BenchmarkVoter::SEARCH
2. Lecteur de Configuration
Le HydraLinksConfigurationReader
parcourt automatiquement tous les fichiers YAML du dossier api_links
et compile la configuration :
public function getLinksConfiguration(): array
{
$linksConfiguration = [];
$finder = new Finder();
$finder->in($projectDir . '/config/api_links')->files()->name('*.yaml');
foreach ($finder as $file) {
$config = Yaml::parseFile($file->getRealPath(), Yaml::PARSE_CONSTANT);
foreach ($config as $key => $value) {
$linksConfiguration[$key] = $value;
}
}
return $linksConfiguration;
}
3. Générateur de Liens
Le HydraLinksGenerator
est le cœur du système. Il :
- Vérifie si l’entité a des liens configurés
- Contrôle les permissions via les Voters Symfony
- Génère les URLs des actions autorisées
public function generate($object): array
{
$resourceClass = str_replace("Proxies\\__CG__\\", '', $object::class);
$links = [];
if (isset($this->apiLinksConfig[$resourceClass])) {
foreach ($this->apiLinksConfig[$resourceClass] as $name => $config) {
if (isset($config['voterAttribute']) &&
$this->authorizationChecker->isGranted($config['voterAttribute'], $object)) {
$links[$name] = $this->generateLink($object, $config);
}
}
}
return $links;
}
4. Normalizer pour l’Injection Automatique
Le HydraLinksNormalizer
intercepte la sérialisation des entités et ajoute automatiquement les liens à la réponse JSON :
public function normalize(mixed $object, string $format = null, array $context = []): array
{
$data = $this->normalizer->normalize($object, $format, $context);
if($this->canNormalizeWithLinks($object, $context)) {
$links = $this->hydraLinksGenerator->generate($object);
$data['hydra:links'] = $links;
}
return $data;
}
Exemples Concrets
Configuration d’un Club
App\Entity\Club:
club:edit:
route: _api_/v2/clubs/{id}{._format}_patch
voterAttribute: !php/const App\Security\Voter\ClubVoter::EDIT
club:delete:
route: _api_/v2/clubs/{id}{._format}_delete
voterAttribute: !php/const App\Security\Voter\ClubVoter::DELETE
club:invitation:
route: _api_/v2/invitations{._format}_post
voterAttribute: !php/const App\Security\Voter\ClubVoter::INVITATION
Réponse API Résultante
Quand un utilisateur fait une requête GET sur /api/clubs/123?withApiLinks=true
, il recevra :
{
"@context": "/api/contexts/Club",
"@id": "/api/clubs/123",
"@type": "Club",
"id": 123,
"name": "Mon Club",
"hydra:links": {
"club:edit": "/api/clubs/123",
"club:invitation": "/api/invitations"
}
}
Note : Le lien club:delete
n’apparaît pas car l’utilisateur n’a pas les permissions nécessaires.
Avantages du Système
1. Découplage Frontend/Backend
Le frontend n’a plus besoin de connaître les règles de permissions – il suffit de vérifier la présence des liens.
2. Sécurité Renforcée
Les permissions sont contrôlées côté serveur via les Voters Symfony, garantissant la cohérence.
3. Évolutivité
Ajouter de nouvelles actions ne nécessite que la mise à jour du fichier YAML correspondant.
4. Standard JSONLD/Hydra
Le système respecte les standards web sémantiques, facilitant l’interopérabilité.
5. Performance
Les liens sont générés en une seule passe lors de la sérialisation, sans requêtes supplémentaires.
Utilisation Côté Client
Activation des Links
Pour activer les API Links, ajoutez le paramètre withApiLinks=true
à vos requêtes :
GET /api/profiles/456?withApiLinks=true
Traitement Frontend
Côté frontend, vous pouvez facilement vérifier les actions disponibles :
// Vérifier si l'édition est autorisée
const canEdit = response['hydra:links']?.['profile:edit'] !== undefined;
// Construire dynamiquement les boutons d'action
const actions = Object.keys(response['hydra:links'] || {});
Intégration dans Symfony
Le système s’intègre naturellement dans Symfony via :
- Compiler Pass : Charge la configuration au build
- Dependency Injection : Injecte la configuration dans les services
- Event System : S’appuie sur les normalizers Symfony
Conclusion
Le système API Links représente une approche moderne et élégante pour gérer les actions disponibles dans une API REST. En combinant la configuration déclarative, les Voters Symfony et les standards web sémantiques, il offre une solution robuste qui simplifie le développement tout en améliorant la sécurité.
Cette approche nous a permis de :
- Réduire le code de gestion des permissions côté frontend
- Améliorer la maintenabilité de l’API
- Offrir une meilleure expérience développeur
- Respecter les standards industriels
Le système API Links illustre parfaitement comment une architecture bien pensée peut transformer un problème complexe en une solution élégante et réutilisable.