sauber aufsetzen — von Null bis Swagger-UI
Wer eine Symfony-REST-API ohne Dokumentation betreibt, überlässt Konsumenten und QA-Teams die mühsame Reverse-Engineering-Arbeit. NelmioApiDocBundle generiert aus PHP-Attributes und YAML-Konfiguration eine vollständige OpenAPI 3.1-Spezifikation – inklusive Swagger-UI, JWT-Security-Schemes und Response-Schemas direkt aus dem Code.
Inhaltsverzeichnis
- 1. Warum NelmioApiDocBundle statt manueller YAML-Specs
- 2. Installation und Bundle-Konfiguration
- 3. OpenAPI-Attributes in Symfony-Controllern
- 4. Request- und Response-Schemas mit PHP-Klassen
- 5. Security-Schemes: JWT Bearer und API-Key konfigurieren
- 6. API-Gruppen und Versionierung
- 7. Swagger-UI produktionssicher bereitstellen
- 8. OpenAPI-Spec als JSON/YAML exportieren und testen
- 9. Konfigurationsansätze im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum NelmioApiDocBundle statt manueller YAML-Specs
Eine REST-API-Dokumentation, die von Hand als YAML-Datei gepflegt wird, veraltet mit dem ersten Commit, der keinen Doc-Update enthält. In der Praxis entstehen so Diskrepanzen zwischen Spec und tatsächlichem Verhalten: Ein Endpunkt erwartet plötzlich ein zusätzliches Pflichtfeld, die Swagger-Datei weiß nichts davon, und der erste Konsument erfährt es per 400-Fehler. NelmioApiDocBundle löst dieses Problem, indem es die OpenAPI-Spezifikation direkt aus dem PHP-Code ableitet — aus Routing-Metadaten, PHP-Attributes an Controllern und typisierten Request-/Response-Klassen.
Der zweite Vorteil liegt in der IDE-Unterstützung. PHP-Attributes sind typsicher, und PhpStorm zeigt Autovervollständigung und Validierungsfehler direkt beim Schreiben. Wer bisher YAML editiert und dabei Tippfehler eingebaut hat, die erst beim Deployment auffielen, schätzt die statische Überprüfbarkeit sofort. Das Bundle integriert sich außerdem nahtlos in Symfony-Security, erkennt firewall-geschützte Routen und kann deren Security-Anforderungen automatisch in die Spec übertragen.
Drittens ist die Swagger-UI, die das Bundle mitbringt, direkt aus dem Symfony-Entwicklungsserver erreichbar und zeigt immer den aktuellen Stand der Spec — ohne separaten Build-Schritt, ohne Datei-Synchronisation. Für Teams, die gleichzeitig an Backend und Frontend arbeiten, ist das ein deutlicher Effizienzgewinn gegenüber statischen Spec-Dateien im Repository.
2. Installation und Bundle-Konfiguration
Die Installation erfolgt über Composer. Das Bundle benötigt mindestens PHP 8.1 und Symfony 6.0. Ab PHP 8.2 empfiehlt sich die Nutzung von PHP-Attributes statt Annotationen, da Annotationen (Doctrine-Stil mit @OA\...) in Symfony 7 nicht mehr bevorzugt werden. Nach der Installation registriert Symfony Flex das Bundle automatisch in config/bundles.php und legt eine Basiskonfiguration unter config/packages/nelmio_api_doc.yaml an.
Die Konfiguration des Bundles trennt sich in zwei Bereiche: die globalen API-Metadaten (Titel, Version, Beschreibung, Server-URLs) und die Routing-Filter, die bestimmen, welche Endpunkte in die Spec aufgenommen werden. Letzteres ist wichtig, um interne Admin-Routen oder Health-Check-Endpunkte aus der öffentlichen Dokumentation auszuschließen. Das Bundle unterstützt mehrere benannte Bereiche (areas), sodass man separate Specs für eine öffentliche API und eine interne Admin-API aus derselben Codebasis generieren kann.
# Install NelmioApiDocBundle via Composer
composer require nelmio/api-doc-bundle
# Install Swagger-UI assets (needed for the web UI)
composer require symfony/asset
# Optionally: install form type support for request body inference
composer require symfony/form
# Generate config skeleton if not auto-generated
php bin/console config:dump nelmio_api_doc
# config/packages/nelmio_api_doc.yaml
nelmio_api_doc:
documentation:
info:
title: "Mironsoft REST API"
description: "Public API for Mironsoft platform services"
version: "1.0.0"
servers:
- url: "https://api.mironsoft.de/v1"
description: "Production"
- url: "http://localhost:8000/v1"
description: "Development"
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
areas:
public:
path_patterns:
- "^/api/v1"
host_patterns: []
admin:
path_patterns:
- "^/api/admin"
3. OpenAPI-Attributes in Symfony-Controllern
Ab PHP 8.0 sind native Attributes die bevorzugte Methode, um OpenAPI-Metadaten direkt an Controller-Methoden zu hinterlegen. Das OpenApi\Attributes-Namespace des zircote/swagger-php-Pakets, das NelmioApiDocBundle intern nutzt, stellt alle nötigen Attribute bereit: #[OA\Get], #[OA\Post], #[OA\Parameter], #[OA\RequestBody] und #[OA\Response]. Diese Attribute werden direkt an der Controller-Action platziert und beschreiben Eingaben, Ausgaben und mögliche Fehlerresponses präzise.
Wichtig ist, den richtigen Abstraktionsgrad zu wählen. Einzelne Parameter wie Pfad-IDs oder Query-Filter gehören als #[OA\Parameter] an die Action. Request-Bodies mit komplexen JSON-Strukturen sollten dagegen auf eigene Schema-Klassen verweisen, die als #[OA\Schema] dekoriert sind. Wer jeden Feldnamen direkt im Controller-Attribute inline beschreibt, erzeugt unlesbare Attribute-Blöcke und verliert die Wiederverwendbarkeit von Schemas über mehrere Endpunkte hinweg.
<?php
// src/Controller/Api/V1/ProductController.php
declare(strict_types=1);
namespace App\Controller\Api\V1;
use OpenApi\Attributes as OA;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;
#[OA\Tag(name: 'Products')]
#[Route('/api/v1/products')]
class ProductController extends AbstractController
{
/**
* Retrieve a single product by its ID.
*/
#[OA\Get(
path: '/api/v1/products/{id}',
summary: 'Get product by ID',
parameters: [
new OA\Parameter(name: 'id', in: 'path', required: true,
schema: new OA\Schema(type: 'integer', minimum: 1))
],
responses: [
new OA\Response(response: 200, description: 'Product found',
content: new OA\JsonContent(ref: '#/components/schemas/ProductResponse')),
new OA\Response(response: 404, description: 'Product not found',
content: new OA\JsonContent(ref: '#/components/schemas/ErrorResponse')),
]
)]
#[Route('/{id}', methods: ['GET'])]
public function show(int $id): JsonResponse
{
// ... controller logic
}
}
4. Request- und Response-Schemas mit PHP-Klassen
Wiederverwendbare OpenAPI-Schemas definiert man am besten als eigene PHP-Klassen oder DTOs, die mit #[OA\Schema] annotiert sind. Jedes Property der Klasse erhält ein #[OA\Property]-Attribute mit Typ, Beschreibung und optionaler Validierungsinformation wie minimum, maxLength oder enum. Diese Klassen leben sinnvollerweise im Namespace App\Api\Schema\ oder App\Dto\, getrennt von Entities und Services.
Ein entscheidender Vorteil dieses Ansatzes: Dieselben DTO-Klassen, die für die OpenAPI-Spec genutzt werden, können auch als Symfony-Form-Typen oder als Ziele für den Symfony-Serializer dienen. Kombiniert mit dem Symfony Validator werden Validierungsregeln und API-Schema an einem einzigen Ort definiert, und beide — Laufzeitvalidierung und generierte Dokumentation — bleiben automatisch synchron. Das ist der Kern des Code-first-Ansatzes gegenüber der Design-first-Alternative.
5. Security-Schemes: JWT Bearer und API-Key konfigurieren
NelmioApiDocBundle unterstützt alle in OpenAPI 3.x definierten Security-Scheme-Typen: HTTP Bearer (für JWT), API-Key (Header oder Query-Parameter) und OAuth2 mit verschiedenen Flows. Die Konfiguration erfolgt entweder global in der YAML-Konfiguration des Bundles (für Scheme-Definitionen) oder pro Endpunkt als #[OA\Security]-Attribute (um anzugeben, welches Scheme der jeweilige Endpunkt erfordert). Routen, die durch Symfonys Firewall mit stateless: true und JWT geschützt sind, müssen explizit mit #[OA\Security(name: 'bearerAuth')] annotiert werden, da das Bundle die Firewall-Konfiguration nicht automatisch auswertet.
In der Swagger-UI erscheint dann oben rechts der Authorize-Button, über den Tester einen JWT-Token eingeben können. Das Token wird für alle nachfolgenden Try-it-out-Anfragen als Authorization-Header mitgesendet. Für interne APIs ist es sinnvoll, einen zweiten Security-Bereich zu definieren, der ein langlebiges API-Key-Scheme nutzt — beispielsweise für Maschine-zu-Maschine-Kommunikation, bei der kein kurzlebiges JWT-Token praktikabel ist.
# config/packages/security.yaml (relevant JWT firewall snippet)
security:
firewalls:
api:
pattern: ^/api/v1
stateless: true
jwt: ~
# In controller action — apply security requirement to endpoint:
# #[OA\Security(name: 'bearerAuth')]
# config/packages/nelmio_api_doc.yaml — global security default
nelmio_api_doc:
documentation:
security:
- bearerAuth: []
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
apiKey:
type: apiKey
in: header
name: X-API-KEY
6. API-Gruppen und Versionierung
Größere APIs bestehen oft aus mehreren Versionen oder logischen Gruppen — eine öffentliche v1-API für externe Konsumenten, eine v2-API mit Breaking Changes und eine Admin-API für interne Verwaltungsoperationen. NelmioApiDocBundle modelliert diese Trennung über benannte areas. Jeder Bereich hat eigene path_patterns, eigene Security-Defaults und wird unter einer eigenen URL bereitgestellt: /api/doc/public.json versus /api/doc/admin.json.
Versionierung im URL-Pfad (/api/v1/ vs. /api/v2/) ist die verbreitetste Strategie und mit dem Bundle einfach umzusetzen. Wer Header-basierte Versionierung (Accept: application/vnd.mironsoft.v2+json) bevorzugt, kann das mit Custom-Request-Matchern implementieren, muss aber die Routing-Patterns entsprechend anpassen. Bei URL-Versionierung empfiehlt sich, die routes in separaten Dateien zu organisieren und über config/routes/api_v1.yaml und config/routes/api_v2.yaml einzubinden, um die area-Filterung des Bundles korrekt zu nutzen.
7. Swagger-UI produktionssicher bereitstellen
Die Swagger-UI von NelmioApiDocBundle ist im Entwicklungsmodus unter /api/doc erreichbar und sofort nutzbar. Im Produktionsbetrieb muss man zwei Dinge beachten: Erstens sollte die Route zur Swagger-UI hinter einem Authentifizierungswall liegen — niemand sollte die vollständige API-Dokumentation inklusive Sicherheits-Schemes ohne Login einsehen können. Zweitens verursacht die dynamische Spec-Generierung bei jedem Request CPU-Last, da das Bundle alle Controller nach Attributes durchsucht. In der Produktion sollte die Spec gecacht oder als statische Datei ausgeliefert werden.
Die einfachste Absicherung ist ein separater Symfony-Firewall-Eintrag für /api/doc, der HTTP-Basic oder eine IP-Whitelist als Authentifizierung nutzt. Für Teams mit CI/CD-Pipeline ist es sinnvoll, die OpenAPI-Spec als Build-Artefakt zu generieren (bin/console nelmio:apidoc:dump --area=public > public/api-spec.json) und die Swagger-UI von einem Nginx aus statisch auszuliefern, der die JSON-Datei referenziert. So entfällt der Runtime-Overhead vollständig.
8. OpenAPI-Spec als JSON/YAML exportieren und testen
NelmioApiDocBundle bringt einen Konsolen-Befehl mit, der die generierte Spec in verschiedene Formate exportiert. bin/console nelmio:apidoc:dump --area=public --format=json gibt die vollständige OpenAPI 3.x-Spezifikation als JSON auf stdout aus. Mit einer Pipe in jq . kann man die Ausgabe formatieren oder mit python3 -m json.tool auf syntaktische Korrektheit prüfen. Der Export in YAML (--format=yaml) ist gut lesbar und eignet sich als Basis für manuelle Ergänzungen oder als Input für andere Tools.
Für automatisierte Validierung in der CI-Pipeline empfiehlt sich swagger-cli validate api-spec.json oder der spectral lint-Befehl aus dem Stoplight-Ökosystem. Spectral unterstützt eigene Regelsets, mit denen man Coding-Guidelines für die API-Spec durchsetzen kann: Pflicht-Felder in Response-Schemas, verbotene Fieldnamen, konsistente Benennungskonventionen. Diese Validierung läuft in Sekunden und verhindert, dass unvollständige oder inkonsistente Specs in den Hauptbranch gelangen.
9. Konfigurationsansätze im Vergleich
Es gibt verschiedene Wege, OpenAPI-Dokumentation in einem Symfony-Projekt zu pflegen. Die Wahl des Ansatzes beeinflusst Wartbarkeit, Vollständigkeit und den Integrationsaufwand erheblich.
| Ansatz | Vorteil | Nachteil | Empfehlung |
|---|---|---|---|
| Manuelle YAML-Spec | Volle Kontrolle, kein PHP-Overhead | Veraltet schnell, Sync-Aufwand hoch | Nur für Design-first mit Code-Generierung |
| PHP Attributes (NelmioApiDocBundle) | Immer aktuell, IDE-Support, Wiederverwendung | Attributes am Controller sind verbose | Empfohlen für bestehende Symfony-Apps |
| API Platform | Vollautomatisch aus Entities | Hohes Opinioning, schwer anpassbar | Gut für CRUD-lastige Ressourcen-APIs |
| Spec-Generator aus Tests | Spec direkt aus realem Verhalten | Komplex, dediziertes Test-Framework nötig | Als Ergänzung zu NelmioApiDocBundle |
| Swagger-PHP allein | Ohne Bundle, leichtgewichtig | Keine Symfony-Integration, manuelle Routen | Nur wenn kein Symfony-Bundle gewünscht |
Für die meisten Symfony-Projekte ist die Kombination aus PHP Attributes mit NelmioApiDocBundle und einer CI-seitigen Spectral-Validierung der pragmatischste Weg. Die Attribute sind nah am Code, die Spec ist immer aktuell, und der Validierungsschritt stellt sicher, dass Qualitätsstandards eingehalten werden — ohne dass ein Entwickler manuell eine YAML-Datei pflegen muss.
Mironsoft
REST API Design, Symfony-Entwicklung und OpenAPI-Dokumentation
Symfony-REST-API mit vollständiger OpenAPI-Dokumentation?
Wir setzen NelmioApiDocBundle in bestehenden Symfony-Projekten auf, migrieren veraltete Annotations zu PHP Attributes und integrieren Spec-Validierung in eure CI/CD-Pipeline.
Bundle-Setup
Installation, Konfiguration, Routing-Filter und Security-Schemes einrichten
Schema-Design
Request- und Response-DTOs mit vollständigen OpenAPI-Schemas und Validierung
CI-Integration
Spectral-Linting, Spec-Export als Artefakt und Swagger-UI produktionssicher absichern
10. Zusammenfassung
NelmioApiDocBundle ist der pragmatischste Weg, OpenAPI 3.1 in Symfony-Projekten zu integrieren, ohne eine separate Spec-Datei pflegen zu müssen. PHP Attributes an Controllern und Schema-Klassen halten Dokumentation und Code synchron. Benannte areas trennen öffentliche und interne APIs sauber voneinander. Security-Schemes für JWT Bearer und API-Key sind in der YAML-Konfiguration des Bundles definiert und über #[OA\Security]-Attribute pro Endpunkt angewendet. Der Konsolen-Export-Befehl macht die Spec als statisches Artefakt für CI-Validierung und produktionsseitige Auslieferung verfügbar.
Der wichtigste Schritt nach der Einrichtung ist die Integration von spectral lint in die CI-Pipeline. Ohne automatische Validierung schleichen sich fehlende Response-Schemas, undokumentierte Fehler-Responses und inkonsistente Feldnamen ein — und genau diese Details sind es, die API-Konsumenten im Integrationsprojekt Zeit kosten. Ein valides OpenAPI-Dokument ist die Basis für Mock-Server, Client-Code-Generierung und Security-Audits, die alle auf derselben Spec aufbauen.
NelmioApiDocBundle — Das Wichtigste auf einen Blick
Installation
composer require nelmio/api-doc-bundle – Symfony Flex legt Konfiguration und Routing automatisch an. PHP 8.1+ und Symfony 6.x erforderlich.
PHP Attributes
#[OA\Get], #[OA\Post], #[OA\Schema] direkt am Controller und DTO – kein YAML-Sync nötig, IDE-Validierung inklusive.
Security
Bearer JWT und API-Key in nelmio_api_doc.yaml definieren, pro Route mit #[OA\Security(name: 'bearerAuth')] anwenden.
Produktion
Spec mit bin/console nelmio:apidoc:dump exportieren, Swagger-UI firewall-geschützt bereitstellen und Spectral-Validierung in CI integrieren.