Die Frage, die mir Symfony-Teams am häufigsten stellen, wenn sie eine neue API beginnen, ist irgendeine Variante von: “Sollen wir API Platform nutzen oder die Controller einfach selbst schreiben?”
Es ist die leicht falsche Frage. Die richtige Frage ist: “Auf welchen Dimensionen sind wir bereit, kurzfristige Geschwindigkeit gegen langfristige Flexibilität zu tauschen, und auf welchen sind wir bereit, langfristige Flexibilität gegen kurzfristige Geschwindigkeit zu tauschen?”
API Platform trifft einen bestimmten Tausch. Es gibt Ihnen kostenlos eine enorme Menge an Verhalten, das sonst Code wäre, den Sie schreiben und pflegen müssten. Im Gegenzug legt es Sie auf eine bestimmte Denkweise über Ressourcen, Operationen und Serialisierung fest und macht manche Arten von Anpassung leicht und andere schmerzhaft.
Selbst gebautes REST trifft den gegenteiligen Tausch. Sie schreiben alles. Sie kontrollieren auch alles. Es gibt keine Lernkurve jenseits von Symfony selbst, und es gibt kein Library-Upgrade zu verwalten, das die Form Ihrer API verändern könnte.
Keine Antwort ist universell richtig. Die Entscheidung hängt davon ab, welche Dimensionen für Ihre konkrete Anwendung, Ihr konkretes Team und Ihren konkreten Zeitplan am wichtigsten sind. Dieser Essay ist ein Rahmen, um diese Entscheidung ehrlich zu treffen.
Die sieben Dimensionen
Ich habe APIs lang genug auf beide Arten gebaut, um mich auf sieben Dimensionen festzulegen, die wirklich einen Unterschied machen. Bewerten Sie Ihr Projekt auf jeder davon, und die Antwort fällt meist heraus.
1. Time to Market
Wenn Sie in weniger als einer Woche eine funktionierende CRUD-API für eine Domain-Entität in Produktion brauchen, ist API Platform schwer zu schlagen.
Eine typische API Platform-Resource:
namespace App\ApiResource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Delete;
#[ApiResource(
operations: [
new Get(),
new GetCollection(),
new Post(),
new Patch(),
new Delete(),
],
paginationItemsPerPage: 20,
)]
class Article
{
public string $title;
public string $body;
public \DateTimeImmutable $publishedAt;
}
Das ist eine vollständige REST-API: Liste mit Pagination, Einzelabruf, Anlegen, partielles Update, Löschen. Mit Hydra-Dokumentation, OpenAPI-Spezifikationsgenerierung, Content Negotiation über JSON-LD, JSON und XML. Konfigurierbare Filter. Validierungsregeln aus Symfonys Validator. Alles, ohne einen einzigen Controller zu schreiben.
Dieselbe Oberfläche in reinem Symfony selbst zu bauen, sind mehrere Tage Arbeit, selbst mit einem Generator. Nicht weil ein einzelnes Stück schwer wäre, sondern weil die kumulierte Arbeit aus Pagination, Validierung, Fehlerantworten, OpenAPI-Dokumentation und konsistenten JSON-Shapes sich summiert.
API Platform gewinnt diese Dimension mit großem Abstand. Wenn Ihr Projekt eine enge Deadline hat und die API-Oberfläche überwiegend ressourcenorientiert ist, kann das allein die Wahl entscheiden.
2. Ressourcen-Fit
API Platform ist um die Idee herum entworfen, dass Ihre API Ressourcen bereitstellt und diese Ressourcen sauber auf Entitäten (oder Entität-artige DTOs) abgebildet sind. Je näher Ihre Domain dieser Form kommt, desto besser der Fit.
Die Form, zu der API Platform gut passt:
- Entitäten mit Attributen, Relationen, Validierungsregeln.
- Operationen, die überwiegend Create, Read, Update, Delete auf diesen Entitäten sind.
- Filter, die überwiegend Gleichheit, Bereich oder Textsuche auf Entitätsfeldern sind.
Die Form, zu der API Platform schlecht passt:
- Operationen, die Commands sind, keine Ressourcen-Manipulationen. “Storniere diese Bestellung” ist kein PATCH auf einer Bestellung, das ist ein Command. Sie können das als State-Mutation modellieren, kämpfen aber gegen das Framework.
- APIs, bei denen die Antwort-Form das Ergebnis einer Berechnung ist, nicht einer serialisierten Entität. Aggregate, Dashboards, Reports.
- APIs, bei denen dieselbe logische Ressource an verschiedenen Endpoints sehr unterschiedliche Formen hat. Ein
User, der am öffentlichen Endpoint 4 Felder zurückgibt und am Admin-Endpoint 47, jeweils mit anderem Security-Modell.
Sie können all das in API Platform mit Custom Operations, Custom Data Providern und Custom Output Classes abbilden. Aber sobald Sie das alles tun, haben Sie eine selbst gebaute REST-API über einer API Platform-Hülle geschrieben, und die Hülle rechnet sich immer weniger.
Bewerten Sie diese Dimension über den Prozentsatz Ihrer Endpoints, die in das Ressourcen-Modell passen. Liegt er über 70 Prozent, ist API Platform vermutlich die richtige Entscheidung. Liegt er unter 40 Prozent, bauen Sie selbst.
3. Anpassungsrate
Das ist die Dimension, die die meisten Teams falsch einschätzen. Sie projizieren ihren aktuellen Anpassungsbedarf in die Zukunft und nehmen an, er bleibe konstant. Das tut er fast nie.
Das Muster ist konsistent: Ein Projekt startet mit überwiegend Standard-CRUD. Nach 18 Monaten hat das Team 30 bis 50 Custom Operations, 20 Custom Normalizer, 15 Custom Filter und ein Verzeichnis voller API Platform-Extensions, das niemand vollständig versteht. Jedes einzelne war für sich genommen die richtige Entscheidung. Zusammen bilden sie eine Anpassungsschicht, die schwerer zu warten ist als die Controller, die sie ersetzt hat.
Eine nützliche Frage: Von den API-Endpoints, die aktuell in Produktion sind, wie viele arbeiten mit den Defaults des Frameworks und wie viele brauchen Custom Code?
// Default API Platform behavior: fine
#[ApiResource(operations: [new Get(), new GetCollection()])]
// Customized: manageable, but starting to leak
#[ApiResource(
operations: [
new Get(
normalizationContext: ['groups' => ['article:read']],
security: "is_granted('ROLE_USER') and object.author == user",
provider: ArticleProvider::class,
processor: ArticleProcessor::class,
),
],
)]
Jedes Stück Anpassung ist eine Stelle, an der das Team API Platform-Interna kennen muss. Custom Data Providers, Custom Processors, Custom Event Subscriber auf KernelEvents::REQUEST, die den API Platform-Kontext manipulieren. Nach einigen davon pflegt das Team faktisch einen Fork des API Platform-Verhaltens in Form eines Extensions-Verzeichnisses.
Wenn Ihre API mehr als eine Handvoll Operationen hat, die substanziell von den Defaults abweichen, ist das ein Signal Richtung selbst gebaut. Nicht weil API Platform das nicht könnte, sondern weil die Kosten dafür innerhalb von API Platform die Kosten in reinem Symfony übersteigen.
4. Dokumentationsbedarf
API Platform generiert eine OpenAPI-Spezifikation, eine Hydra-Dokumentation, eine Swagger-UI, eine ReDoc-UI und (mit den passenden Bundles) einen GraphQL-Playground, alles aus Ihren Resource-Definitionen. Die Dokumentation bleibt synchron mit dem Code, weil sie der Code ist.
Selbst gebautes REST heißt, dass Sie die OpenAPI-Spezifikation separat schreiben, entweder von Hand (was auseinanderläuft) oder mit Annotationen wie nelmio/api-doc-bundle (was meistens funktioniert, aber eigene Annotationen zur Pflege mitbringt).
Wenn Ihre API von externen Entwicklern, Partnern oder einem Mobile-Team konsumiert wird, das Sie nicht täglich sehen, spielt die Dokumentationslücke eine große Rolle. Automatisch generierte Docs, die immer korrekt sind, sind ein echtes Geschenk.
Wenn Ihre API nur von Ihrem eigenen Frontend-Team konsumiert wird, das neben Ihnen sitzt, spielt die Lücke weniger Rolle. Sie können ihnen den Controller zeigen.
Bewerten Sie diese Dimension nach der Zielgruppe. Extern oder verteilt: starkes Gewicht für API Platform. Intern und nebenan: schwaches Signal.
5. Performance-Charakteristik
Das Default-Request-Handling von API Platform hat mehr Schritte als ein selbst gebauter Controller: Routing, Deserialisierung, Denormalisierung, Validierung, Operation-Logik, Normalisierung, Serialisierung, Dokumentations-Hooks. Jeder Schritt ist schnell, aber es sind mehr davon.
Für die meisten APIs ist das unsichtbar. Ein typischer API Platform-Endpoint fügt im Vergleich zum handgefertigten Äquivalent 10 bis 30 ms Requestzeit hinzu. Auf einer nicht kritischen internen API ist das Rauschen.
Bei manchen APIs ist es nicht unsichtbar. Konkret:
- Hochvolumige Endpoints (mehrere tausend Requests pro Sekunde).
- Endpoints mit sehr großen Antwort-Payloads, bei denen die Serialisierung dominiert.
- Endpoints hinter einer harten SLA (unter 50 ms p99).
Dort gibt Ihnen selbst Gebautes direkte Kontrolle darüber, was pro Request getan wird. Sie können Serialisierungsschritte überspringen, rohe JSON-Strings zurückgeben, Antworten vorberechnen. Nichts davon ist in API Platform unmöglich, aber alles kämpft gegen das Framework.
Das ist die Dimension, bei der Teams Performance überbewerten und alles andere unterbewerten. Die meisten APIs haben keine 50-ms-SLA. Die meisten APIs sind mit einem Median von 100 ms zufrieden. Seien Sie ehrlich über Ihre tatsächlichen Performance-Anforderungen, bevor Sie diese Dimension die Entscheidung kippen lassen.
6. Team-Skills und Kontinuität
API Platform ist ein eigenes Framework auf Symfony aufgesetzt. Um es gut zu nutzen, muss das Team lernen:
- Das Metadaten-Modell (
ApiResource,Get,Post,Patchetc.) - Das State-Pattern (Providers und Processors)
- Die Serialisierungs-Pipeline (Normalizers, Denormalizers, Contexts, Groups)
- Das Extension-System (Filter Extensions, Query Extensions, Security Extensions)
- Wie API Platform mit Symfonys eigenen Komponenten interagiert (Security, Validation, Messenger)
Das sind reale Kosten. Ein neuer Engineer im Team verbringt Wochen damit, API Platform-Konventionen zusätzlich zu Symfony zu lernen. Die Tiefe des Verständnisses, das man braucht, um ein Serialisierungsproblem zu debuggen, ist deutlich größer als die Tiefe, die man für einen selbst gebauten Controller braucht.
Im Gegenzug wird das Team, das API Platform gut lernt, darin sehr produktiv. Neue Endpoints dauern Minuten. Standard-Anpassungen werden zur Routine.
Seien Sie ehrlich, ob Ihr Team in das Erlernen des Frameworks investieren wird. Wenn ja, zahlt sich die Investition schnell aus. Wenn nein, enden Sie mit einer halb gelernten API Platform-Anwendung, in der das Team 20 Prozent des Frameworks nutzt und um den Rest herumschreibt, was das Schlechteste aus beiden Welten ist.
7. Langzeit-Ownership
APIs leben lange. Die Entscheidung, die Sie heute treffen, legt Sie auf eine Wartungshaltung fest, so lange die API Konsumenten hat, was meist länger ist, als man denkt.
Das Langzeit-Ownership-Profil von API Platform:
- Major-Versionen kommen alle ein bis zwei Jahre und brachten in der Vergangenheit einige Breaking Changes mit. Das Team muss das einplanen.
- Die Library ist gesund und gut gewartet, mit aktiven Beiträgern und klarer Roadmap.
- Der Bus-Faktor ist akzeptabel, aber nicht null. Wenn die Maintainer aussteigen, haben Sie ein Fork-Problem.
- Die Konventionen, die API Platform codiert (HATEOAS, JSON-LD, Hydra), sind stabil, aber nicht universell. Manche Konsumenten verstehen sie nicht und brauchen konfigurierten Plain-JSON-Output.
Das Langzeit-Ownership-Profil von selbst gebautem REST:
- Kein Upgrade-Risiko jenseits von Symfony selbst.
- Keine externe Maintainer-Abhängigkeit.
- Jede Zeile Verhalten ist Sache Ihres Teams. Dazu gehören Dinge, die Sie nicht erwartet haben zu pflegen (Pagination-Edgecases, Drift der OpenAPI-Spezifikation, Konsistenz der Fehler-Envelopes).
- Die Konventionen sind das, worauf sich Ihr Team geeinigt hat, was auch heißt: alles, worauf sich Ihr Team zu einigen vergessen hat. Eine selbst gebaute API entwickelt über die Zeit Inkonsistenzen, weil verschiedene Entwickler verschiedene Entscheidungen treffen.
Für eine API, die voraussichtlich länger als drei Jahre lebt und externe Konsumenten hat, haben beide Optionen Ownership-Kosten; es sind nur andere Kosten. Wählen Sie die, die Ihr Team besser tragen kann.
Die Entscheidungsmatrix
Bewerten Sie Ihr Projekt auf jeder Dimension. Wählen Sie die Option, die mehr gewichtete Dimensionen gewinnt, wobei Sie die Dimensionen stärker gewichten, die in Ihrem Kontext am wichtigsten sind.
| Dimension | API Platform gewinnt, wenn | Selbst gebaut gewinnt, wenn |
|---|---|---|
| Time to Market | Enge Deadline, überwiegend CRUD | Kein Deadline-Druck, volle Kontrolle gewünscht |
| Ressourcen-Fit | >70 Prozent der Endpoints passen ins Modell | <40 Prozent der Endpoints passen ins Modell |
| Anpassungsrate | Überwiegend reichen die Defaults | Viele Endpoints weichen substanziell ab |
| Dokumentation | Externe oder verteilte Konsumenten | Interne, direkt benachbarte Konsumenten |
| Performance | Keine SLA unter 50 ms | Hot Path, große Payloads, harte SLAs |
| Team-Skills | Team investiert ins Erlernen | Team investiert nicht oder hat hohe Fluktuation |
| Langzeit-Ownership | Framework-Upgrades sind okay | Minimale externe Abhängigkeiten gewünscht |
Die ehrliche Zusammenfassung: API Platform ist die richtige Antwort für die meisten internen, CRUD-förmigen APIs in etablierten Symfony-Shops. Selbst gebaut ist die richtige Antwort für performance-kritische APIs, command-förmige APIs oder Projekte, in denen das Team nicht in ein zweites Framework investieren wird.
Durchgerechnete Beispiele
Um das konkret zu machen: drei reale Projektformen und was ich jeweils wählen würde.
Projekt A: eine Admin-Panel-API für ein internes SaaS
- 40 Entitäten, überwiegend geradliniges CRUD.
- Konsumiert von einem Frontend-Team, das im selben Raum sitzt.
- Performance-Budget: ein paar hundert Millisekunden sind in Ordnung.
- Team hat Erfahrung mit API Platform.
Wahl: API Platform. Das ist der kanonische Fit. Das Team schreibt 40 Resources in zwei Tagen und verbringt den Rest des Projekts mit tatsächlicher Fachlogik, nicht mit Pagination-Klempnerei.
Projekt B: ein öffentliches Mobile-App-Backend mit Bezahlflüssen
- 25 Endpoints, aber überwiegend command-förmig (Bestellung aufgeben, stornieren, Zahlung anfechten, Rückerstattung anfordern).
- Strikte SLAs auf dem Pfad der Bestellaufgabe (unter 100 ms p99).
- Konsumiert von einem externen Mobile-Team, das Integrationscode gegen eine OpenAPI-Spezifikation schreibt.
- Team hat Symfony-Erfahrung, aber keine API Platform-Erfahrung.
Wahl: selbst gebaut, mit nelmio/api-doc-bundle für die OpenAPI-Generierung. Die Command-Form, das Performance-Budget und das unvertraute Framework schieben alle weg von API Platform. Der OpenAPI-Bedarf lässt sich mit Nelmio über kontrollierte Annotationen bedienen.
Projekt C: eine mandantenfähige Headless-CMS-API
- 60 Entitäten, alle CRUD-förmig.
- Konsumiert von einer unbekannten Anzahl externer Entwickler, die individuelle Frontends bauen.
- Performance-Budget: 200 ms p95 sind akzeptabel.
- Team ist klein und stabil.
Wahl: API Platform. Die Kombination aus Ressourcenform, großer Entitätszahl und externen Entwicklern, die OpenAPI-Dokumentation brauchen, macht API Platform sehr produktiv. Das Team kann ins Erlernen investieren, weil es nicht ausgewechselt wird, und das Framework zahlt sich über alle 60 Entitäten hinweg aus.
Was Teams falsch machen
Drei Muster, die ich konsistent sehe:
API Platform wählen, weil es die modische Antwort ist. In manchen Symfony-Kreisen ist es die modische Antwort, und das ist kein Grund. Wenn Ihr Projekt überwiegend command-förmig ist, bauen Sie selbst. Wenn Ihr Team nicht investiert, bauen Sie selbst. Die Popularität des Frameworks in der Symfony-Community ist für sich kein hinreichender Grund, sich auf seine Konventionen festzulegen.
Selbst gebaut wählen, weil das Team Magie misstraut. Manche Teams haben eine kulturelle Vorliebe für expliziten Code, den sie Zeile für Zeile lesen können. Das ist eine legitime Vorliebe, sollte aber hinterfragt werden: Selbst gebautes REST, das Pagination, Validierung, Fehler-Envelopes und OpenAPI-Generierung neu implementiert, ist nicht weniger Magie, es ist nur mehr Code. Wenn die Magie das ist, was API Platform überhaupt funktionieren lässt, kauft Ihnen die Abwesenheit dieser Magie keine Einfachheit, sondern nur Umfang.
Sich auf eins festlegen und nicht mischen. Beide Optionen können in derselben Anwendung koexistieren. Ein typisches Muster: API Platform für die 70 Prozent CRUD-Endpoints, selbst gebaute Controller für die command-förmigen oder performance-kritischen, gemountet unter verschiedenen Route-Präfixen. Das Team, das sich dogmatisch auf eine Seite festlegt, kämpft am Ende auf den Endpoints, die nicht passen, gegen das Framework.
Wenn keine der beiden Optionen richtig ist
Manche APIs wollen gar kein REST sein. Der obige Entscheidungsrahmen setzt voraus, dass Sie sich bereits für REST entschieden haben. Falls nicht:
- GraphQL ist die richtige Antwort, wenn Konsumenten die Form der Antwort selbst angeben sollen und die API viele mögliche Projektionen hat. API Platform unterstützt GraphQL, aber ein dedizierter GraphQL-Stack (
webonyx/graphql-phpmitoverblog/GraphQLBundle) passt häufig besser. - JSON-RPC ist die richtige Antwort, wenn die API überwiegend command-förmig ist und die Ressourcen-Perspektive erzwungen wirkt. Eine kleine Menge benannter Operationen mit typisierten Ein- und Ausgaben ist näher an dem, was die Anwendung tatsächlich tut.
- gRPC ist die richtige Antwort, wenn die Konsumenten andere Backend-Services sind und der Kontrakt von strikter Schema-Erzwingung profitiert. Symfony hat keinen First-Class-Support für gRPC, lässt sich aber mit vertretbarem Aufwand erweitern.
Die REST-Entscheidung ist nicht die einzige Entscheidung. Wählen Sie die Protokollform, bevor Sie die Implementierungsstrategie wählen.
Die Kurzfassung
Falls Sie nichts anderes lesen: API Platform für ressourcenförmige, CRUD-lastige, extern dokumentierte APIs in Teams, die in das Framework investieren. Selbst gebaut für command-förmige, performance-kritische, intern konsumierte APIs in Teams, die das nicht tun. Mischen Sie frei, wenn die Anwendung beide Formen hat. Lassen Sie die Framework-Wahl nicht zu einem Glaubensbekenntnis werden.
Wenn Sie eine neue API starten und bei dieser Entscheidung im Kontext Ihres konkreten Projekts Hilfe wollen, beinhaltet mein Business-Alignment-Engagement einen einwöchigen API-Entscheidungssprint, der Ihre Anforderungen gegen diesen Rahmen bewertet und einen kalkulierten Implementierungsplan in der jeweils sinnvollen Richtung liefert.
Referenzen
- API Platform Dokumentation: offizieller Einstiegspfad zum hier durchgängig diskutierten REST/GraphQL-Framework.
- Hydra Core Vocabulary (W3C Community Group): das Hypermedia-Vokabular, das API Platform standardmäßig ausgibt.
- JSON-LD: das Linked-Data-Format, das API Platform für seine Default-Repräsentationen nutzt.
- NelmioApiDocBundle: das Symfony-Bundle, das für die OpenAPI-Generierung auf selbst gebauten Controllern empfohlen wird.
- webonyx/graphql-php: die Referenzimplementierung der GraphQL-Spezifikation in PHP.
- overblog/GraphQLBundle: das Symfony-Bundle, das
graphql-phpin eine Anwendung einbindet. - JSON-RPC 2.0 Specification: die kanonische Spezifikation, auf die der Abschnitt “wenn keine der beiden Optionen richtig ist” verweist.
- gRPC: das hochperformante RPC-Framework, das als Service-zu-Service-Alternative zu REST genannt wird.