welcher GraphQL-Ansatz zu welchem Team passt
Bei Schema-First entsteht die SDL-Datei zuerst und wird zum Vertrag zwischen Frontend und Backend. Bei Code-First generiert eine Bibliothek das Schema aus Klassen und Decorators heraus. Beide Wege führen zu einer funktionierenden GraphQL-API, doch sie unterscheiden sich fundamental in Tooling, Teamstruktur und Wartbarkeit über die Zeit.
Inhaltsverzeichnis
- 1. Schema-First vs. Code-First: zwei Philosophien
- 2. Schema-First: die SDL als Vertrag zwischen Teams
- 3. Code-First: das Schema aus dem Code generieren
- 4. Tooling-Ökosystem im Vergleich
- 5. Teamstruktur: wer profitiert von welchem Ansatz?
- 6. Schema-Evolution und Versionierung in beiden Modellen
- 7. Typsicherheit: Codegen vs. native Typen
- 8. Migrationspfade zwischen den Ansätzen
- 9. Schema-First und Code-First im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Schema-First vs. Code-First: zwei Philosophien
Wer eine neue GraphQL-API aufsetzt, trifft früh eine Architekturentscheidung, die sich später nur schwer rückgängig machen lässt: Wird das Schema zuerst als SDL-Datei geschrieben (Schema-First), oder entsteht es automatisch aus Klassen, Decorators und Annotationen im Anwendungscode (Code-First)? Beide Ansätze erzeugen am Ende dieselbe Introspection-fähige GraphQL-API, aber der Weg dorthin unterscheidet sich fundamental, und diese Entscheidung prägt jahrelang, wie ein Team über seine API nachdenkt.
Der Kern des Unterschieds liegt in der Frage: Was ist die Quelle der Wahrheit? Bei Schema-First ist es eine lesbare .graphqls-Datei, die vor der Implementierung existiert und von Frontend- und Backend-Entwicklern gemeinsam verhandelt werden kann. Bei Code-First ist es die Programmiersprache selbst: Klassen, Typen und Decorators definieren das Schema, das erst zur Laufzeit oder beim Build generiert wird. Beide Philosophien haben in großen Produktionssystemen ihre Berechtigung, und die folgenden Abschnitte zeigen, wo genau die Vor- und Nachteile liegen.
2. Schema-First: die SDL als Vertrag zwischen Teams
Beim Schema-First-Ansatz schreibt ein Entwickler zunächst die Schema Definition Language, bevor auch nur eine Zeile Resolver-Code existiert. Diese SDL-Datei wird zum verbindlichen Vertrag: Frontend-Teams können gegen das Schema entwickeln, sobald es steht, unabhängig davon, ob die Resolver-Implementierung bereits fertig ist. Mock-Server wie graphql-tools oder Apollo Server mit addMocksToSchema erzeugen aus der reinen SDL-Datei sofort funktionsfähige Testdaten, ohne dass Backend-Code existieren muss. Das entkoppelt Frontend- und Backend-Entwicklung zeitlich, was besonders bei parallelen Sprints wertvoll ist.
Magentos eigene GraphQL-Implementierung folgt konsequent dem Schema-First-Prinzip: Jedes Modul bringt eine schema.graphqls-Datei mit, die über di.xml mit Resolver-Klassen verknüpft wird. Diese Trennung zwingt Entwickler, das Schema als eigenständiges Artefakt zu behandeln, das in Code-Reviews separat betrachtet werden kann. Ein Nachteil zeigt sich allerdings bei wachsenden Schemas: Die SDL-Datei und die Resolver-Klasse driften auseinander, wenn niemand konsequent prüft, ob jedes SDL-Feld auch tatsächlich einen Resolver hat, denn die Kopplung ist nur durch Konvention und Konfiguration gesichert, nicht durch den Compiler.
# schema.graphqls — Schema-First: SDL is written before any resolver exists
type Product {
id: ID!
sku: String!
name: String!
price: Money!
# Nullable on purpose — not every product has a manufacturer set
manufacturer: Manufacturer
reviews(first: Int = 10, after: String): ReviewConnection!
}
type Money {
amount: Float!
currency: CurrencyEnum!
}
type Query {
product(sku: String!): Product
products(filter: ProductFilterInput, pageSize: Int = 20): ProductConnection!
}
input ProductFilterInput {
category: String
minPrice: Float
maxPrice: Float
}
3. Code-First: das Schema aus dem Code generieren
Der Code-First-Ansatz dreht die Reihenfolge um: Entwickler schreiben Klassen, Interfaces oder Decorators in ihrer Anwendungssprache, und eine Bibliothek generiert daraus zur Laufzeit oder beim Build das GraphQL-Schema. In PHP übernimmt das graphql-php im Code-First-Modus über ObjectType-Definitionen, in TypeScript erledigen das TypeGraphQL mit Decorators oder Nexus mit einer deklarativen Builder-API. Der große Vorteil: Es gibt keine zweite Wahrheitsquelle, die synchron gehalten werden muss. Ändert sich eine Klasse, ändert sich automatisch auch das Schema, denn beide sind identisch.
Diese Eigenschaft macht Code-First besonders attraktiv für Teams, die bereits stark typisierte Backend-Sprachen wie TypeScript oder PHP mit striktem Typing nutzen. Ein Entwickler, der eine neue Property zu einer TypeScript-Klasse hinzufügt, sieht sofortige Compiler-Fehler, wenn ein Resolver diese Property falsch nutzt, ganz ohne manuelles Schema-Update. Der Nachteil: Das Schema existiert nicht mehr als eigenständiges, lesbares Artefakt, das Frontend-Teams vorab verhandeln können. Wer die finale SDL sehen will, muss den Code generieren lassen oder ein Introspection-Tool bemühen, was die frühe Abstimmung zwischen API-Konsument und API-Anbieter erschwert.
// product.type.ts — Code-First with TypeGraphQL: schema is generated from classes
import { ObjectType, Field, ID, Float, Resolver, Query, Arg } from 'type-graphql';
@ObjectType()
class Money {
@Field(() => Float)
amount: number;
@Field()
currency: string;
}
@ObjectType()
class Product {
@Field(() => ID)
id: string;
@Field()
sku: string;
@Field()
name: string;
@Field(() => Money)
price: Money;
// Nullable field — TypeScript's optional marker maps directly to GraphQL nullability
@Field(() => String, { nullable: true })
manufacturer?: string;
}
@Resolver(Product)
class ProductResolver {
@Query(() => Product, { nullable: true })
async product(@Arg('sku') sku: string): Promise<Product | null> {
return productRepository.findBySku(sku);
}
}
// ProductType.php — Code-First with webonyx/graphql-php: schema built from PHP classes
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
final class ProductType extends ObjectType
{
public function __construct()
{
parent::__construct([
'name' => 'Product',
'fields' => fn(): array => [
'id' => Type::nonNull(Type::id()),
'sku' => Type::nonNull(Type::string()),
'name' => Type::nonNull(Type::string()),
'price' => Type::nonNull(MoneyType::instance()),
// Nullable — no separate SDL file to keep in sync
'manufacturer' => Type::string(),
],
'resolveField' => function (Product $product, array $args, $context, $info) {
return $product->{$info->fieldName} ?? null;
},
]);
}
}
4. Tooling-Ökosystem im Vergleich
Das Tooling rund um Schema-First ist historisch das reifere Ökosystem, weil GraphQL selbst mit einer textuellen Schema-Sprache eingeführt wurde. Tools wie GraphQL Code Generator lesen die SDL-Datei und erzeugen daraus typsichere Resolver-Signaturen, Frontend-Hooks und sogar vollständige TypeScript-Interfaces. Schema-Linter wie graphql-schema-linter prüfen Namenskonventionen und Best Practices direkt gegen die SDL-Datei, ohne dass Anwendungscode ausgeführt werden muss. Auch Schema-Registries wie Apollo Studio oder Hive arbeiten primär mit der SDL als Artefakt, das versioniert und verglichen wird.
Für Code-First hat sich in den letzten Jahren ein eigenständiges, ebenfalls ausgereiftes Ökosystem entwickelt: Nexus, Pothos und TypeGraphQL in der TypeScript-Welt, Lighthouse mit Attributen in Laravel, sowie diverse Annotation-basierte Ansätze in Java und Kotlin. Diese Tools generieren die SDL als Nebenprodukt und exportieren sie meist als Datei für externe Konsumenten, sodass Frontend-Tooling trotzdem funktioniert. Der Unterschied liegt im Workflow: Bei Code-First ist die generierte SDL ein Build-Artefakt, das committed oder bei jedem Build neu erzeugt wird, während sie bei Schema-First die primäre, von Hand gepflegte Quelle ist.
5. Teamstruktur: wer profitiert von welchem Ansatz?
Die Wahl zwischen Schema-First und Code-First hängt stark von der Teamstruktur ab. Größere Organisationen mit getrennten Frontend- und Backend-Teams profitieren fast immer von Schema-First, weil die SDL-Datei als eigenständiger Verhandlungsgegenstand in Design-Reviews funktioniert, lange bevor Backend-Code existiert. Ein Frontend-Team kann gegen einen Mock-Server entwickeln, während das Backend-Team parallel die Resolver implementiert, und beide Seiten synchronisieren sich über die Datei im Git-Repository, nicht über Absprachen in Meetings.
Kleinere Teams oder Full-Stack-Entwickler, die sowohl Frontend als auch Backend in derselben Sprache schreiben, etwa TypeScript in einem Node.js-Monorepo, profitieren häufiger von Code-First. Hier entfällt der Koordinationsaufwand zwischen zwei getrennten Artefakten, und ein einziger Entwickler kann eine neue Funktion von der Datenbank bis zum Resolver durchziehen, ohne zwischen SDL-Datei und Implementierungscode hin und her zu wechseln. In gemischten PHP-Projekten wie Magento hingegen dominiert Schema-First, weil die Modul-Architektur ohnehin auf deklarative XML- und Schema-Dateien setzt, die unabhängig vom PHP-Code versioniert werden.
6. Schema-Evolution und Versionierung in beiden Modellen
Schema-Evolution, also das kontrollierte Hinzufügen, Verändern und Entfernen von Feldern über die Zeit, funktioniert bei Schema-First transparent über Diffs der SDL-Datei. Ein Pull-Request, der ein Feld entfernt, zeigt genau diese eine Zeile als Löschung, was Reviewer sofort erkennen lässt, ob es sich um eine Breaking Change handelt. CI-Pipelines können automatisiert die alte gegen die neue SDL-Datei prüfen und bei inkompatiblen Änderungen den Merge blockieren, bevor ein Konsument betroffen ist.
Bei Code-First ist Schema-Evolution subtiler zu überwachen, weil sich das Schema erst nach dem Build oder zur Laufzeit manifestiert. Ein Refactoring, das eine TypeScript-Klasse umbenennt, kann unabsichtlich ein GraphQL-Feld verändern, ohne dass der Entwickler das als API-Änderung wahrnimmt. Deshalb ist es bei Code-First-Projekten Pflicht, die generierte SDL als Artefakt zu exportieren und ebenfalls in Diff-Tools zu prüfen, sonst verliert man genau den Überblick, den Schema-First von Haus aus bietet.
#!/usr/bin/env bash
# ci-schema-diff.sh — works for both approaches once SDL is exported as an artifact
set -euo pipefail
# Schema-First: SDL file is already the source of truth
# Code-First: export the generated schema first, e.g. `ts-node export-schema.ts`
CURRENT_SCHEMA="schema.graphqls"
BASELINE_SCHEMA="$(git show origin/main:schema.graphqls)"
diff <(echo "$BASELINE_SCHEMA") "$CURRENT_SCHEMA" > /tmp/schema.diff || true
if grep -qE '^< .*(type|field)' /tmp/schema.diff; then
echo "[WARN] Possible breaking change detected — review required" >&2
cat /tmp/schema.diff
exit 1
fi
echo "[OK] Schema change is additive"
7. Typsicherheit: Codegen vs. native Typen
Typsicherheit wird bei Schema-First durch einen zusätzlichen Generierungsschritt erreicht: GraphQL Code Generator liest die SDL-Datei und erzeugt TypeScript-Interfaces oder PHP-Stub-Klassen, die im Resolver-Code verwendet werden. Das funktioniert gut, erfordert aber, dass der Codegen-Schritt bei jeder Schema-Änderung erneut ausgeführt wird, sonst driften generierte Typen und tatsächliches Schema auseinander. Viele Teams integrieren diesen Schritt in einen Git-Hook oder eine CI-Prüfung, die den Build fehlschlagen lässt, wenn generierte Typen veraltet sind.
Bei Code-First ist Typsicherheit von Natur aus gegeben, weil das Schema direkt aus typisiertem Code entsteht. Ein TypeScript-Compiler-Fehler bei falscher Feldnutzung tritt sofort auf, ohne einen separaten Codegen-Lauf. Dieser Vorteil kehrt sich allerdings um, sobald Frontend-Konsumenten ins Spiel kommen, die nicht denselben Code-First-Stack nutzen: Sie benötigen weiterhin eine exportierte SDL-Datei und im Zweifel denselben Codegen-Schritt wie bei Schema-First, wodurch der vermeintliche Vorteil bei Cross-Team-APIs teilweise wieder verschwindet.
# codegen.yml — GraphQL Code Generator config, works against exported SDL
# from EITHER approach (Schema-First file or Code-First export)
schema: "schema.graphqls"
documents: "src/**/*.graphql"
generates:
src/generated/graphql.ts:
plugins:
- typescript
- typescript-operations
- typescript-react-apollo
config:
withHooks: true
scalars:
Money: "{ amount: number; currency: string }"
8. Migrationspfade zwischen den Ansätzen
Ein vollständiger Wechsel von Code-First zu Schema-First gelingt meist schrittweise: Zunächst wird die generierte SDL-Datei exportiert und als eigenständiges Artefakt ins Repository committed. Anschließend übernimmt diese Datei die Rolle der Quelle der Wahrheit, während die Code-First-Bibliothek durch klassische Resolver-Bindungen ersetzt wird, Modul für Modul, nicht auf einen Schlag. Diese Migration lohnt sich typischerweise, wenn ein Projekt wächst und mehrere unabhängige Frontend-Teams hinzukommen, die eine stabile, verhandelbare Schnittstelle brauchen.
Der umgekehrte Weg, von Schema-First zu Code-First, ist seltener, kommt aber vor, wenn ein Team feststellt, dass die manuelle Synchronisation zwischen SDL-Datei und Resolver-Code zu viele Fehler produziert. Hier hilft es, zunächst ein einzelnes, klar abgegrenztes Modul probeweise auf Code-First umzustellen und die Erfahrung im Team zu sammeln, bevor eine größere Umstellung angegangen wird. In beiden Richtungen gilt: Ein Big-Bang-Wechsel des gesamten Schemas an einem Tag ist riskant, während eine modulweise Migration mit exportierter SDL als Sicherheitsnetz zuverlässig funktioniert.
9. Schema-First und Code-First im direkten Vergleich
Die folgende Tabelle fasst die zentralen Unterschiede zwischen Schema-First und Code-First zusammen, basierend auf den in diesem Artikel besprochenen Kriterien Tooling, Teamstruktur und Wartbarkeit.
| Kriterium | Schema-First | Code-First |
|---|---|---|
| Quelle der Wahrheit | SDL-Datei, von Hand gepflegt | Anwendungscode, SDL generiert |
| Frühe Frontend/Backend-Trennung | Sehr gut, Mock-Server sofort nutzbar | Nur mit zusätzlichem SDL-Export |
| Typsicherheit im Resolver | Erfordert Codegen-Schritt | Nativ durch typisierten Code |
| Risiko von Drift | SDL und Resolver können auseinanderlaufen | Ausgeschlossen, beide identisch |
| Ideal für Teamgröße | Große, getrennte Teams | Kleine Full-Stack-Teams |
| Typisches Beispiel | Magento GraphQL, Apollo mit SDL | TypeGraphQL, Nexus, Pothos |
Kein Ansatz ist pauschal überlegen. Die Tabelle zeigt, dass Schema-First bei Teamkoordination und expliziter API-Verhandlung punktet, während Code-First bei Typsicherheit und Vermeidung von Drift die Nase vorn hat. Viele produktive Systeme kombinieren beide Prinzipien: Ein Code-First-Backend exportiert seine SDL als versioniertes Artefakt und behandelt es anschließend wie eine Schema-First-Datei für alle externen Konsumenten.
Mironsoft
GraphQL-Architektur, Schema-Design und API-Strategie
Den richtigen GraphQL-Ansatz für euer Team finden?
Wir bewerten eure Teamstruktur, bestehende Tooling-Landschaft und Wachstumspläne und empfehlen konkret, ob Schema-First oder Code-First für euer nächstes GraphQL-Projekt die bessere Wahl ist.
Architektur-Review
Bestehende Schema-Strategie analysieren und Verbesserungspotenzial aufzeigen
Migration
Schrittweisen Wechsel zwischen Schema-First und Code-First begleiten
Tooling-Setup
Codegen, Schema-Linting und CI-Diff-Checks passend zum Ansatz aufsetzen
10. Zusammenfassung
Schema-First und Code-First lösen dasselbe Problem, die Definition eines GraphQL-Schemas, mit gegensätzlichen Prioritäten. Schema-First behandelt die SDL-Datei als eigenständigen Vertrag, der früh zwischen Teams verhandelt werden kann und sich sauber diffen lässt, erfordert aber Disziplin, um Drift zwischen Schema und Resolver-Code zu vermeiden. Code-First eliminiert diese Drift-Gefahr durch native Typsicherheit, verlangt aber einen zusätzlichen Export-Schritt, sobald externe Konsumenten eine lesbare SDL benötigen.
Die richtige Entscheidung hängt weniger von technischer Überlegenheit ab als von der Teamstruktur: Getrennte Frontend- und Backend-Teams mit unterschiedlichen Sprachen fahren meist besser mit Schema-First, während Full-Stack-Teams in einer einzigen typisierten Sprache von Code-First profitieren. Magento und viele Enterprise-PHP-Systeme setzen aus historischen und architektonischen Gründen konsequent auf Schema-First, während moderne TypeScript-Monorepos zunehmend Code-First bevorzugen.
Schema-First vs. Code-First — Das Wichtigste auf einen Blick
Schema-First
SDL-Datei als verhandelbarer Vertrag, ideal für getrennte Teams, erfordert Codegen für Typsicherheit.
Code-First
Schema aus typisiertem Code generiert, kein Drift-Risiko, aber schwerer für externe Teams verhandelbar.
Entscheidungskriterium
Teamstruktur und Sprachlandschaft entscheiden, nicht technische Überlegenheit eines Ansatzes.
Hybrid-Option
Code-First mit exportierter SDL vereint native Typsicherheit und verhandelbaren Vertrag.