GraphQL ist eine Abfragesprache für APIs und eine serverseitige Runtime, die in einer Anfrage genau die Felder liefert, die ein Client anfordert. Die Doppelnatur ist entscheidend: Die Sprache ist eine Spezifikation, die jeder Client sprechen kann; die Runtime führt diese Queries gegen ein Typsystem aus, das Sie über Ihren vorhandenen Daten definieren – jede Datenbank, jeder Dienst. GraphQL ist keine Datenbank und ersetzt weder Ihren Speicher noch zwangsläufig Ihre REST-API.
Das Wichtigste in Kürze
| Frage | Antwort |
|---|---|
| Was es ist | Eine spezifizierte Abfragesprache + Ausführungs-Runtime – speicherunabhängig |
| Das Markenzeichen | Die Form der Antwort spiegelt die Form der Query: Felder anfragen, genau diese Felder erhalten |
| Die drei Operationen | Query (lesen) · Mutation (schreiben) · Subscription (Echtzeit-Push) |
| Die Bausteine | Schema (SDL-Vertrag) · Typen · Resolver (Abruffunktionen pro Feld) |
| Die ehrliche Rechnung | Caching-Strategie, N+1-Batching, kostenbasierte Limits, Security-Härtung |
Die typische Demo: Query und Antwort
Die Demo, auf die jede Erklärung hinausläuft, weil sie die Idee ist – die Antwort ist die ausgefüllte Query:
# Anfrage # Antwort
{ {
post(id: "8fk2") { "data": {
title "post": {
author { "title": "Hello GraphQL",
username "author": {
} "username": "ada"
comments(first: 2) { },
text "comments": [
} { "text": "Nice." },
} { "text": "Ship it." }
} ]
}
}
}
Eine Anfrage, drei zusammenhängende Ressourcen, kein einziges unangefordertes Feld – das Paar aus Overfetching und Underfetching ist mit einem Schlag erledigt. Der Aufruf aus echten Clients ist schlichtes HTTP:
// JavaScript / Node.js — query Back4app's auto-generated GraphQL API
const res = await fetch('https://parseapi.back4app.com/graphql', {
method: 'POST',
headers: {
'X-Parse-Application-Id': APP_ID,
'X-Parse-Client-Key': CLIENT_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: '{ posts(first: 20) { edges { node { title author { username } } } } }',
}),
});
const { data } = await res.json(); // shaped exactly like the query // Flutter / Dart — query Back4app's auto-generated GraphQL API
final res = await http.post(
Uri.parse('https://parseapi.back4app.com/graphql'),
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Client-Key': clientKey,
'Content-Type': 'application/json',
},
body: jsonEncode({
'query': '{ posts(first: 20) { edges { node { title author { username } } } } }',
}),
);
final data = jsonDecode(res.body)['data']; // shaped exactly like the query // iOS / Swift — query Back4app's auto-generated GraphQL API
var request = URLRequest(url: URL(string: "https://parseapi.back4app.com/graphql")!)
request.httpMethod = "POST"
request.setValue(appId, forHTTPHeaderField: "X-Parse-Application-Id")
request.setValue(clientKey, forHTTPHeaderField: "X-Parse-Client-Key")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let query = "{ posts(first: 20) { edges { node { title author { username } } } } }"
request.httpBody = try JSONEncoder().encode(["query": query])
let (data, _) = try await URLSession.shared.data(for: request)
// data is shaped exactly like the query // Android / Kotlin — query Back4app's auto-generated GraphQL API
val body = """{ "query": "{ posts(first: 20) { edges { node { title author { username } } } } }" }"""
val request = Request.Builder()
.url("https://parseapi.back4app.com/graphql")
.addHeader("X-Parse-Application-Id", APP_ID)
.addHeader("X-Parse-Client-Key", CLIENT_KEY)
.post(body.toRequestBody("application/json".toMediaType()))
.build()
val data = client.newCall(request).execute().body?.string() // shaped like the query Schema, Query, Resolver: das Arbeitstrio
Erklärungen zeigen die Query; kaum eine zeigt die Maschinerie dahinter als zusammenhängendes Bild. Das Schema ist der typisierte Vertrag, geschrieben in SDL:
type Post {
title: String! # ! = nicht null
author: User!
comments(first: Int): [Comment!]
}
type Query { # die Einstiegspunkte zum Lesen
post(id: ID!): Post
}
type Mutation { # die Einstiegspunkte zum Schreiben
createPost(title: String!): Post!
}
Resolver sind die andere Hälfte der Runtime – eine Funktion pro Feld, jede frei, ihre Daten von überall zu holen:
const resolvers = {
Query: {
post: (_, { id }) => db.posts.findById(id),
},
Post: {
author: (post) => db.users.findById(post.authorId), // pro Post aufgerufen!
},
};
Die Ausführung ist eine Pipeline: Query parsen, gegen das Schema validieren (ungültige Operationen scheitern, bevor sie Daten berühren), dann das Selection Set durchlaufen, Resolver aufrufen und das gespiegelte JSON zusammensetzen. Dieser Resolver-Aufruf pro Feld ist zugleich der Preis der Flexibilität – beachten Sie das // pro Post aufgerufen!, aus dem weiter unten das N+1-Problem wird. Der Name, den niemand erklärt: Ihre Daten bilden einen Graphen aus typisierten Objekten, und Queries durchlaufen ihn von den Root-Feldern aus – allerdings nur entlang der Pfade, die das Schema freigibt, nicht beliebig wie in einer echten Graph-Abfragesprache.
Kurz zur Herkunft: 2012 bei Facebook (heute Meta) für die eigenen Mobile-Apps entwickelt, 2015 als Open Source veröffentlicht, seit 2018 von der GraphQL Foundation unter dem Dach der Linux Foundation verwaltet; die aktuelle Spezifikation ist die Ausgabe vom Oktober 2021, und ein GraphQL-over-HTTP-Entwurf standardisiert die Transportkonventionen.
Queries, Mutations, Subscriptions
Queries lesen. Mutations schreiben – und wählen Felder aus dem Ergebnis aus, sodass der Client den Zustand nach dem Schreiben im selben Roundtrip erhält. Subscriptions halten eine Verbindung offen (in der Praxis WebSockets) und pushen Ereignisse, sobald sie eintreten; sie sind GraphQLs Antwort auf Echtzeit, mit dem Vorbehalt, dass jede aktive Subscription Zustand auf dem Server bindet. Alle drei teilen sich Schema, Typsystem und Tooling – ein Vertrag, drei Zeitformen.
GraphQL vs. REST
| GraphQL | REST | |
|---|---|---|
| Endpoints | Einer (/graphql) | Einer pro Ressource |
| Form der Antwort | Vom Client pro Query zusammengestellt | Pro Endpoint festgelegt |
| Over-/Underfetching | Auf HTTP-Ebene gelöst | Durch Parameter abgemildert |
| HTTP-Caching | Standardmäßig verloren (ein einziger POST) | Nativ – die Superkraft |
| Typisierung & Introspection | Im Vertrag eingebaut | Optional über OpenAPI |
| Versionierung | Versionslose Evolution + @deprecated | Konventionen wie /v1, /v2 |
| Echtzeit | Subscriptions in der Spezifikation | Nicht vorgesehen |
| Stärken | Vielfältige Clients, verschachtelte Daten | Ressourcen-CRUD, cachebare Lesezugriffe |
Die vollständige Abwägung – einschließlich der Fälle, in denen REST schlicht die bessere Wahl ist – steht im eigenen Eintrag GraphQL vs. REST.
GraphQL in Produktion: die ehrlichen Kosten
Der Abschnitt, den Erklärungen von Anbietern abschwächen. Caching: Ein einziger POST-Endpoint verzichtet auf URL-basiertes HTTP- und CDN-Caching; Ersatz sind normalisierte Client-Caches, die auf id plus __typename schlüsseln, sowie Persisted Queries (freigegebene, gehashte Operationen, die als GET gesendet werden), um einen Teil des Transport-Cachings zurückzugewinnen. N+1: Naive Resolver machen aus einer Liste von 20 Posts 1 + 20 Datenbankzugriffe – dasselbe Problem, das REST-Clients über HTTP haben, nur in Ihre Resolver-Schicht verlagert und dort durch Batching-Loader wie DataLoader behoben. Fehler: GraphQL antwortet mit 200 OK und einem errors-Array – ein Monitoring, das auf Statuscodes basiert, ist blind, solange man es nicht anders einrichtet. Rate Limiting: Anfragen sind nicht gleich, wenn eine Query zehn Relationen verschachteln kann; ausgereifte APIs messen die Query-Kosten (Tiefen- und Komplexitätsanalyse), nicht die Zahl der Anfragen. Security: Deaktivieren Sie Introspection in Produktion, erzwingen Sie Tiefen- und Komplexitätslimits und belassen Sie die Autorisierung in der Geschäftslogik unterhalb der Resolver – der einzelne Endpoint macht außerdem URL-basierte WAF-Regeln blind, sodass die Validierung in die GraphQL-Schicht selbst wandert.
Typische Anwendungsfälle
- Mobile Apps in eingeschränkten Netzen – der Gründungsanwendungsfall: exakte Felder, minimale Bytes, weniger Roundtrips.
- Produkte mit mehreren Clients – Smartwatch-App, Smartphone-App, Web-Dashboard: Jeder Client formt seine eigenen Antworten gegen ein Schema.
- Aggregation als Backend for Frontend – eine GraphQL-Schicht, die mehrere interne Dienste für die Oberflächen zusammenführt.
- Sich schnell entwickelnde Frontends – neue Screens wählen neue Felder aus, ohne auf neue Endpoints zu warten.
- Typisierte Verträge von Ende zu Ende – Schema-Introspection generiert typisierte Clients und hält API und Oberfläche schon beim Kompilieren synchron.
Sollten Sie GraphQL verwenden? Eine Entscheidungsmatrix
| GraphQL lohnt seine Maschinerie, wenn… | Greifen Sie zu REST, wenn… |
|---|---|
| Clients unterschiedliche Daten brauchen | Es einen Client-Typ mit stabilen Screens gibt |
| Screens verschachtelte, relationale Daten lesen | Ressourcen sich sauber auf Endpoints abbilden lassen |
| Mehrere Backend-Quellen zusammengeführt werden | Ein einziger Dienst die Daten besitzt |
| Bandbreite kostbar ist (Mobile-first) | HTTP-/CDN-Caching die Leselast tragen kann |
| Eine Plattform das Schema für Sie generiert | Das Team alles selbst bauen und härten muss |
Grenzen und Trade-offs
- Die Flexibilität bezahlt der Server. Beliebige Client-Queries bedeuten, dass der Server bei jeder Form sicher bleiben muss – Batching, Kostenlimits und Tiefenbegrenzungen sind Voraussetzungen, kein Feinschliff.
- Caching wird zu Ihrem Projekt. Was HTTP REST gratis mitliefert, bauen GraphQL-Teams in Client-Caches und Persisted Queries nach.
- Observability muss neu gelernt werden. Ein Endpoint, Antworten mit durchgehend 200 und Zeitmessung pro Feld verlangen GraphQL-fähiges Tooling.
- Datei-Uploads und Binärdaten sind umständlich – meist werden sie an separate Upload-Endpoints neben dem Graphen ausgelagert.
- Schema-Governance ist eine Organisationsfrage. Ein teamübergreifender Vertrag braucht Zuständigkeitsregeln; Federation (Zusammensetzen von Subgraphen einzelner Teams zu einem Supergraphen) ist die Antwort auf Skalierung – und eine eigene Disziplin.
GraphQL mit Back4app
Back4app ist eine Open-Source-Plattform für Backend as a Service (BaaS), die eine verwaltete Datenbank, automatisch generierte REST- und GraphQL-APIs, Authentifizierung, Dateispeicher und Serverless-Funktionen mit Cloud Code kombiniert. Das Besondere ist die Herkunft des Schemas: Sie definieren ein Datenmodell, und die Plattform generiert die GraphQL-API – typisierte Objekttypen, Query- und Mutation-Felder, Connections, die Relationen durchlaufen wie im Beispiel posts → author oben –, ohne dass Sie Resolver schreiben müssen, denn Back4app implementiert sie gegen Ihre Datenbank und setzt die Berechtigungen pro Anfrage durch. Die Code-Tabs zeigen die gesamte Client-Seite: ein POST an /graphql mit den Schlüsseln Ihrer App. Eine eingebaute GraphQL-Konsole dient zum Erkunden, REST bleibt für cachefreundliche Lesezugriffe auf denselben Daten verfügbar, und eigene Logik kommt als Cloud-Code-Funktion ins Schema – die ehrlichen Kosten aus dem Abschnitt oben trägt damit größtenteils die Plattform, nicht Sie.
Häufige Fragen
Was ist GraphQL, einfach erklärt?
Eine Abfragesprache, mit der ein Client eine API in einer einzigen Anfrage nach genau den Feldern fragt, die er braucht – verschachtelte Relationen eingeschlossen –, plus eine Server-Runtime, die diese Anfragen aus Ihren vorhandenen Datenquellen bedient. GraphQL sitzt vor einer beliebigen Datenbank oder einem Dienst; es ist selbst keine Datenbank.
Ist GraphQL besser als REST?
Keines von beiden ist grundsätzlich besser. GraphQL gewinnt bei vielfältigen Clients, knapper Bandbreite und Daten, die aus mehreren Quellen zusammengeführt werden; REST gewinnt bei HTTP-Caching, Einfachheit und ausgereiftem Tooling für ressourcenförmiges CRUD. Das vorherrschende Muster in der Produktion ist pragmatisch: eine GraphQL-Schicht für Frontends über REST- oder RPC-Diensten im Inneren.
Ist GraphQL eine Datenbank oder so etwas wie SQL?
Nein – es ist eine API-Sprache auf Anwendungsebene und bewusst speicherunabhängig: Resolver können aus jeder Datenbank, einer anderen API oder einer Datei lesen. Und trotz des Namens ist es keine allgemeine Graph-Abfragesprache wie SPARQL; Sie durchlaufen den Graphen nur entlang der Pfade, die das Schema freigibt.
Was sind Queries, Mutations und Subscriptions?
Die drei Operationstypen. Queries lesen Daten; Mutations schreiben sie – und liefern den neuen Zustand im selben Roundtrip zurück, sodass der Client ohne Folgeabruf aktualisiert; Subscriptions pushen Echtzeit-Updates über eine dauerhafte Verbindung, typischerweise WebSockets. Alle drei werden gegen dasselbe Schema validiert.
Was ist ein GraphQL-Schema?
Der typisierte Vertrag zwischen Client und Server, geschrieben in der Schema Definition Language: Objekttypen, ihre Felder und die Einstiegspunkte Query, Mutation und Subscription. Jede eingehende Operation wird vor der Ausführung dagegen validiert, und Werkzeuge lesen es per Introspection aus, um Dokumentation und typisierte Clients zu generieren.
Was ist ein Resolver?
Eine serverseitige Funktion, die den Wert eines einzelnen Felds beschafft – aus einer Datenbank, einer anderen API oder von irgendwoher. Die Runtime durchläuft jede Query und ruft für jedes angeforderte Feld den Resolver auf. Darin liegt die Flexibilität von GraphQL – und der Ursprung seines N+1-Problems, wenn die Resolver von Listenelementen jeweils einzeln abfragen.
Funktioniert GraphQL nur über HTTP POST?
Laut Spezifikation ist GraphQL transportunabhängig; in der Praxis wird es unter einem einzigen HTTP-Endpoint bereitgestellt – üblicherweise /graphql –, meist per POST mit JSON-Body, wobei GET für Queries erlaubt ist und WebSockets die Subscriptions tragen. Eine GraphQL-over-HTTP-Spezifikation standardisiert diese Konventionen inzwischen.
Wann sollten Sie GraphQL NICHT verwenden?
Bei einfachem Ressourcen-CRUD mit einheitlichen Clients, bei Lesetraffic, den HTTP- und CDN-Caching auffangen könnten, bei dateilastiger Übertragung und in kleinen Teams ohne Kapazität für Resolver-Batching, Begrenzung der Query-Kosten und Schema-Governance. In diesen Fällen liefert eine gut entworfene REST-API dasselbe Ergebnis mit weniger Maschinerie.