die schrittweise Strategie ohne Big Bang
Eine bestehende REST-API auf einen Schlag durch GraphQL zu ersetzen, ist der sicherste Weg, Produktionsausfälle zu provozieren. Mit einer BFF-Schicht, dem Strangler-Fig-Pattern und einem klaren Rollout-Plan lässt sich REST zu GraphQL migrieren, während beide Protokolle monatelang parallel und risikofrei koexistieren.
Inhaltsverzeichnis
- 1. Warum ein Big Bang bei der GraphQL-Migration scheitert
- 2. Die BFF-Schicht als sicherer Startpunkt
- 3. Strangler-Fig-Pattern: REST-Endpunkte schrittweise ablösen
- 4. Schema-first: das GraphQL-Schema vor dem Code entwerfen
- 5. Resolver als dünne Adapter über bestehende REST-Endpunkte
- 6. Parallele Koexistenz: REST und GraphQL im selben System
- 7. Clients schrittweise umstellen ohne Breaking Changes
- 8. REST-Endpunkte sicher abschalten: Traffic-getriebene Entscheidungen
- 9. Migrationsstrategien im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum ein Big Bang bei der GraphQL-Migration scheitert
Der Wunsch, eine gewachsene REST-API komplett durch GraphQL zu ersetzen, ist verständlich, aber der direkte Weg dorthin scheitert in der Praxis fast immer. Wer REST zu GraphQL migrieren will, indem alle Endpunkte gleichzeitig neu gebaut und alle Clients an einem Stichtag umgestellt werden, riskiert einen mehrwöchigen Freeze für neue Features, während parallel Regressionen in produktionskritischen Bereichen entstehen. Der Big Bang zwingt dazu, jede Fachlogik ein zweites Mal zu testen, bevor der alte Pfad überhaupt abgeschaltet werden darf.
Die Alternative ist eine inkrementelle Strategie, bei der REST und GraphQL über Monate hinweg nebeneinander existieren, während Traffic Stück für Stück von REST auf GraphQL wandert. Wer REST zu GraphQL migrieren als kontinuierlichen Prozess statt als Projekt mit festem Enddatum versteht, kann jederzeit anhalten, wenn Kapazität fehlt, und jederzeit weitermachen, sobald wieder Zeit ist. Diese Flexibilität ist der eigentliche Wert einer schrittweisen Migration, nicht nur die reduzierte Risikofläche.
2. Die BFF-Schicht als sicherer Startpunkt
Der praktikabelste erste Schritt, um REST zu GraphQL migrieren zu starten, ist eine Backend-for-Frontend-Schicht, die GraphQL nach außen spricht und im Inneren die bestehenden REST-Endpunkte aufruft. Diese BFF-Schicht ändert an den Backend-Services zunächst nichts, sie fügt lediglich eine Übersetzungsebene hinzu. Frontend-Teams können sofort GraphQL-Queries schreiben, obwohl die eigentlichen Daten noch aus denselben REST-Diensten kommen, die seit Jahren produktiv laufen.
Dieser Ansatz reduziert das Risiko drastisch, weil die fachliche Logik in den Backend-Services unverändert bleibt. Der BFF übernimmt ausschließlich Aggregation, Typumwandlung und Feld-Selektion, klassische GraphQL-Stärken, die REST nicht bietet. Erst in einem zweiten Schritt, wenn die BFF-Schicht stabil läuft, werden einzelne REST-Aufrufe im Resolver durch direkte Datenbankzugriffe oder native GraphQL-Services ersetzt, ohne dass Clients davon etwas mitbekommen.
# BFF schema: mirrors what the REST API already exposes,
# just reshaped into a typed, queryable graph
type Order {
id: ID!
status: OrderStatus!
items: [OrderItem!]!
customer: Customer!
total: Money!
}
type Query {
order(id: ID!): Order
orders(customerId: ID!, limit: Int = 20): [Order!]!
}
// BFF resolver: thin adapter calling the existing REST endpoint,
// no business logic duplicated here
const resolvers = {
Query: {
order: async (_parent, { id }, { restClient }) => {
// Still hits the legacy REST endpoint under the hood
const response = await restClient.get(`/api/orders/${id}`);
return mapRestOrderToGraphQL(response.data);
},
},
};
function mapRestOrderToGraphQL(restOrder: RestOrder): Order {
return {
id: restOrder.order_id,
status: restOrder.status.toUpperCase(),
items: restOrder.line_items.map(mapLineItem),
customer: { id: restOrder.customer_id, name: restOrder.customer_name },
total: { amount: restOrder.grand_total, currency: restOrder.currency_code },
};
}
3. Strangler-Fig-Pattern: REST-Endpunkte schrittweise ablösen
Das Strangler-Fig-Pattern, benannt nach dem Würgefeigenbaum, der einen Wirtsbaum langsam umschließt und ersetzt, beschreibt genau die Migrationslogik hinter REST zu GraphQL migrieren: Neue Funktionalität entsteht direkt in GraphQL, bestehende Funktionalität wird Endpunkt für Endpunkt, Feld für Feld, in den GraphQL-Layer hineingezogen, während der alte REST-Code so lange bestehen bleibt, bis kein Resolver mehr auf ihn zugreift. Der alte Baum stirbt erst, wenn er komplett umschlossen ist.
Entscheidend ist die Reihenfolge: Man beginnt nicht mit dem komplexesten Endpunkt, sondern mit einem gut abgegrenzten, wenig frequentierten Bereich, um Prozess und Tooling risikoarm zu etablieren. Erst wenn dieser erste Slice erfolgreich produktiv läuft, folgt der nächste, größere Bereich. Diese Reihenfolge macht jede Migrationsphase zu einem eigenständigen, rückrollbaren Release, statt zu einem monolithischen Umbau, der als Ganzes funktionieren oder scheitern muss.
4. Schema-first: das GraphQL-Schema vor dem Code entwerfen
Bei einer Migration ist die Versuchung groß, das GraphQL-Schema eins zu eins aus den bestehenden REST-Response-Strukturen abzuleiten. Das führt aber dazu, dass historische REST-Altlasten, inkonsistente Feldnamen, redundante IDs, verschachtelte Wrapper-Objekte, unverändert in das neue Schema übernommen werden. Wer REST zu GraphQL migrieren richtig angeht, entwirft das Zielschema stattdessen unabhängig von der aktuellen REST-Struktur, orientiert an dem, was Clients tatsächlich benötigen.
Dieser Schema-first-Ansatz bedeutet in der Praxis: Erst mit den Frontend-Teams die ideale Query-Form festlegen, dann rückwärts die Resolver-Implementierung bauen, die diese Form aus den vorhandenen REST-Daten zusammensetzt. Die Übersetzungsarbeit landet komplett im Resolver, nicht im Schema. Das erzeugt zwar kurzfristig mehr Mapping-Code, verhindert aber, dass technische Schulden aus der REST-Ära dauerhaft im neuen GraphQL-Vertrag zementiert werden.
# Target schema designed independently from legacy REST shape -
# clean naming, no redundant wrapper objects, proper types
type Customer {
id: ID!
email: String!
fullName: String!
addresses: [Address!]!
loyaltyTier: LoyaltyTier!
}
# Legacy REST response for comparison (what the resolver translates FROM):
# {
# "cust_id": "8842",
# "email_addr": "jane@example.com",
# "first_name": "Jane",
# "last_name": "Doe",
# "addr_list": { "items": [...], "count": 2 },
# "tier_code": "GOLD_02"
# }
5. Resolver als dünne Adapter über bestehende REST-Endpunkte
In der Übergangsphase sind GraphQL-Resolver bei dieser Migration bewusst dünn gehalten: Sie rufen bestehende REST-Endpunkte auf, transformieren die Antwort in die Zielform und geben sie zurück. Keine neue Fachlogik entsteht im Resolver selbst, sie bleibt vollständig im bestehenden Backend-Service. Diese Disziplin verhindert, dass sich Geschäftsregeln während der Migration dupliziert an zwei Stellen wiederfinden, was später zu inkonsistentem Verhalten zwischen REST und GraphQL führen würde.
Ein zentraler Fallstrick bei dieser Umsetzung ist N+1: Ruft ein GraphQL-Resolver für eine Liste von Bestellungen pro Bestellung erneut einen REST-Endpunkt auf, entstehen hunderte HTTP-Requests, wo die alte REST-API einen einzigen Response mit Embedded-Daten geliefert hat. Ein DataLoader, der REST-Aufrufe pro Request-Zyklus batcht und dedupliziert, ist deshalb ab dem ersten produktiven Resolver Pflicht, nicht optionale Optimierung für später.
// DataLoader wraps the legacy REST endpoint to prevent N+1
// during the transition period, before native batching exists
import DataLoader from "dataloader";
const customerLoader = new DataLoader<string, Customer>(async (ids) => {
// Legacy REST endpoint supports batch fetch via query param
const response = await restClient.get("/api/customers", {
params: { ids: ids.join(",") },
});
const byId = new Map(response.data.map((c: RestCustomer) => [c.cust_id, c]));
return ids.map((id) => mapRestCustomerToGraphQL(byId.get(id)));
});
6. Parallele Koexistenz: REST und GraphQL im selben System
Für den gesamten Migrationszeitraum, oft sechs bis achtzehn Monate, müssen REST und GraphQL denselben Datenbestand konsistent bedienen. Wer REST zu GraphQL migrieren ohne Datenintegritätsprobleme durchführen will, muss sicherstellen, dass Schreiboperationen über beide Pfade dieselbe Validierung und dieselben Nebenwirkungen auslösen, etwa Event-Publishing oder Cache-Invalidierung. Ein Mutation-Resolver, der Bestelldaten direkt in der Datenbank ändert, während der parallele REST-Endpunkt weiterhin ein Event auslöst, erzeugt sonst stille Inkonsistenzen.
Der pragmatischste Weg ist, auch Mutations zunächst über den bestehenden REST-Write-Pfad zu leiten, genau wie bei Queries, und erst später eigenständige Schreiblogik im GraphQL-Layer zu implementieren. Das verzögert zwar die vollständige Ablösung von REST intern, garantiert aber, dass während der gesamten Koexistenzphase nur ein einziger Ort existiert, an dem Geschäftsregeln für Schreiboperationen gepflegt werden.
7. Clients schrittweise umstellen ohne Breaking Changes
Die Umstellung der Clients selbst ist der Teil der Migration, der am stärksten mit dem Rest des Teams abgestimmt werden muss. Statt einer App-weiten Umstellung, die einen einzigen Deploy-Tag erzwingt, sollte REST zu GraphQL migrieren auf Feature-Ebene erfolgen: Ein neues Feature wird direkt mit GraphQL gebaut, ein bestehender Screen wird bei der nächsten ohnehin geplanten Überarbeitung umgestellt, kritische Legacy-Bereiche bleiben bewusst bei REST, bis ihre Migration einen klaren fachlichen Anlass hat.
Feature Flags sind dabei das wichtigste technische Hilfsmittel: Ein Flag pro migriertem Screen erlaubt, GraphQL für einen Teil der Nutzer zu aktivieren, Fehlerraten und Latenzen zu vergleichen, und im Problemfall sofort auf REST zurückzufallen, ohne einen neuen Client-Release auszurollen. Diese Rückfalloption ist während der gesamten Übergangszeit der wichtigste Sicherheitsmechanismus der gesamten Strategie.
8. REST-Endpunkte sicher abschalten: Traffic-getriebene Entscheidungen
Der letzte und oft unterschätzte Schritt ist die tatsächliche Abschaltung alter REST-Endpunkte. Ohne belastbare Traffic-Daten neigen Teams dazu, Endpunkte aus reiner Vorsicht jahrelang am Leben zu erhalten, obwohl kein Client mehr darauf zugreift. Wer REST zu GraphQL migrieren sauber abschließen will, instrumentiert jeden REST-Endpunkt mit Zugriffsmetriken, bevor überhaupt migriert wird, und definiert einen klaren Schwellenwert, etwa null Requests über dreißig Tage, ab dem ein Endpunkt als abschaltbar gilt.
Ein zweistufiges Vorgehen hat sich bewährt: Zunächst wird der Endpunkt mit einem Deprecation-Header versehen, der in Response-Headern und Logs sichtbar ist, danach folgt eine Übergangsfrist von typischerweise vier bis acht Wochen, in der verbleibende Aufrufer identifiziert und aktiv kontaktiert werden. Erst danach wird der Endpunkt entfernt, nie ohne vorherige Beobachtungsphase, unabhängig davon, wie sicher man sich fühlt, dass niemand mehr zugreift.
9. Migrationsstrategien im direkten Vergleich
Nicht jede Migrationsstrategie passt zu jedem Team. Die folgende Tabelle stellt die gängigen Ansätze bei REST zu GraphQL migrieren gegenüber.
| Strategie | Risiko | Zeitaufwand | Geeignet für |
|---|---|---|---|
| Big Bang Rewrite | Sehr hoch | Kurz, aber riskant konzentriert | Kleine, wenig kritische Systeme |
| BFF mit REST-Passthrough | Niedrig | Sofort startbereit | Produktionskritische Legacy-Systeme |
| Strangler Fig, Slice für Slice | Niedrig bis mittel | Lang, aber planbar | Große, komplexe REST-Landschaften |
| Paralleler Neubau ohne BFF | Hoch | Doppelte Wartung während Übergang | Selten empfehlenswert |
Die Kombination aus BFF-Einstieg und Strangler-Fig-Fortsetzung liefert in der Praxis das beste Verhältnis aus Risiko und Geschwindigkeit. Sie erlaubt, sofort mit GraphQL-Queries zu arbeiten, ohne auf eine vollständige Backend-Migration warten zu müssen, und schafft gleichzeitig einen klaren, iterativen Pfad zur vollständigen Ablösung von REST.
Mironsoft
GraphQL-Migration, BFF-Architektur und Magento-Integration
Eure REST-API schrittweise auf GraphQL umstellen?
Wir entwerfen mit euch einen risikoarmen Migrationsplan, bauen die BFF-Schicht als sicheren Einstieg und begleiten den Strangler-Fig-Rollout bis zur vollständigen Ablösung eurer Legacy-Endpunkte.
Migrationsplan
Slice-Priorisierung, Traffic-Analyse und Rollout-Reihenfolge für eure REST-Landschaft
BFF-Implementierung
GraphQL-Schema-Design und dünne Resolver über bestehende REST-Endpunkte
Client-Umstellung
Feature-Flag-gesteuerte Migration eurer Frontend-Teams ohne Breaking Changes
10. Zusammenfassung
Wer REST zu GraphQL migrieren will, sollte den Big Bang von Anfang an ausschließen. Eine BFF-Schicht, die GraphQL nach außen spricht und intern bestehende REST-Endpunkte aufruft, ermöglicht sofortigen Start ohne Backend-Risiko. Das Strangler-Fig-Pattern zieht Funktionalität Slice für Slice in den neuen Layer, während der alte Code so lange bestehen bleibt, bis kein Resolver mehr auf ihn angewiesen ist.
Entscheidend für den Erfolg sind drei Disziplinen: ein schema-first entworfenes Zielschema statt einer Eins-zu-eins-Übernahme der REST-Struktur, konsequentes DataLoader-Batching gegen N+1 in Übergangsresolvern, und Traffic-Metriken als einzige verlässliche Grundlage für die endgültige Abschaltung alter Endpunkte. Wer diese drei Punkte beachtet, kann REST zu GraphQL migrieren, ohne jemals einen riskanten Cutover-Tag einplanen zu müssen.
REST zu GraphQL migrieren — Das Wichtigste auf einen Blick
BFF als Einstieg
GraphQL-Schicht vor bestehenden REST-Endpunkten, kein Backend-Umbau nötig, sofort startbereit.
Strangler Fig
Slice für Slice migrieren, jede Phase eigenständig releasbar und rückrollbar.
Schema-first
Zielschema unabhängig von REST-Altlasten entwerfen, Mapping-Arbeit gehört in den Resolver.
Traffic-getriebene Abschaltung
Erst Metriken sammeln, dann deprecaten, erst danach Endpunkte tatsächlich entfernen.