GraphQL#
GraphQL is a query language for HTTP APIs and the matching server runtime. A single endpoint serves a typed schema; the client asks for exactly the fields it wants and gets back exactly that. Two languages live inside one technology: the Schema Definition Language (SDL) on the server side, and the query language on the client side.
GraphQL fits between REST and gRPC. REST is resource-oriented and untyped at the wire; gRPC is RPC-shaped and binary; GraphQL is type-driven, schema-first, JSON over HTTP. Common in product APIs (GitHub, Shopify, the public Anthropic console API), less common in internal service-to-service traffic.
SDL#
The schema is the server’s contract. Types, fields, the relationships between them, the operations clients can run.
type User {
id: ID!
name: String!
email: String
posts: [Post!]!
}
type Post {
id: ID!
title: String!
body: String
author: User!
}
type Query {
user(id: ID!): User
posts(limit: Int = 10): [Post!]!
}
type Mutation {
createPost(title: String!, body: String): Post!
deletePost(id: ID!): Boolean!
}
type Subscription {
postAdded: Post!
}
Type markers.
Marker |
Meaning |
|---|---|
|
scalar type (also |
|
non-null. |
|
list of |
|
object type. |
|
input object (only for arguments). |
|
enumeration. |
|
polymorphism (object types that share fields, or value that can be one of several types). |
The three root types Query, Mutation, and
Subscription declare what clients can send.
Query#
A client picks fields out of the schema. The structure of the response mirrors the structure of the query.
query {
user(id: "42") {
name
email
posts {
title
}
}
}
Response.
{
"data": {
"user": {
"name": "rk",
"email": "rk@example.com",
"posts": [
{"title": "Hello"},
{"title": "Operator's notes"}
]
}
}
}
Variables, aliases, fragments.
query GetUser($id: ID!) {
primary: user(id: $id) { # alias
...UserSummary # fragment spread
}
}
fragment UserSummary on User {
id
name
email
}
# variables sent alongside the request body
# { "id": "42" }
Mutation#
A write operation. Same structure as a query; the convention is to ask for the new state in the response so the client can update its cache.
mutation {
createPost(title: "Hello", body: "world") {
id
title
author { name }
}
}
Subscription#
A server-pushed stream. Typically over WebSocket
(graphql-ws protocol). The client subscribes; the server
emits events that match the subscription’s structure.
subscription {
postAdded {
id
title
}
}
Directives#
Annotations on queries and schemas. Two built-in.
query GetUser($skipEmail: Boolean!) {
user(id: "42") {
name
email @skip(if: $skipEmail)
posts @include(if: false) # always omitted
}
}
Schema directives (@deprecated, @auth, @cacheControl)
attach metadata that the server or gateway reads.
N+1 problem#
The cost of GraphQL’s “fetch what you want” model is that a
single query can fan out to many database round-trips.
DataLoader (and its ports across languages) batches and
caches per-request to dedup these.
// Node example
const userLoader = new DataLoader(async (ids) => {
const users = await db.users.findMany({ where: { id: { in: ids } } });
return ids.map(id => users.find(u => u.id === id));
});
Every GraphQL server framework ships some flavour of this.
Federation#
Apollo Federation and the open-source GraphQL Federation spec compose multiple subgraphs (one per microservice) into one supergraph behind a single endpoint. Each subgraph owns its types; the gateway plans the cross-service query.
Server libs#
Stack |
Library |
|---|---|
JS / TS |
|
Python |
Strawberry (typed), Graphene, Ariadne (schema-first). |
Go |
gqlgen (schema-first, codegen), graphql-go. |
Rust |
|
Java / Kotlin |
graphql-java, DGS (Netflix). |
Client libs#
Stack |
Library |
|---|---|
JS / TS |
Apollo Client, urql, Relay, graphql-request (minimal). |
Python |
gql. |
Go |
genqlient (typed), graphql-go/client. |
Generic |
any HTTP client (POST JSON to |
For typed clients, the workflow is schema + queries →
generated code: write .graphql files alongside the source,
generate types and resolver bindings, import them.
Tooling#
GraphQL Code Generator, generates types and operation hooks from schema + queries.
GraphiQL, the in-browser REPL most servers expose at
/graphql.Apollo Studio / Hasura, hosted explorer / gateway / observability.
Inigo, WunderGraph, gateways and governance.
GraphQL vs. REST vs. gRPC#
REST |
GraphQL |
gRPC |
|
|---|---|---|---|
Style |
resource |
schema-driven query |
typed RPC |
Wire |
JSON over HTTP |
JSON over HTTP |
protobuf over HTTP/2 |
Endpoint structure |
many URLs |
one URL, query selects |
one URL per method |
Versioning |
URL or header |
additive schema evolution |
protobuf tags + reserved |
Browser-friendly |
native |
native |
via gRPC-Web / Connect |
Reach for it |
simple CRUD APIs, public |
product APIs with rich client-driven structures, many client variations |
internal service-to-service traffic, polyglot teams |
References#
Cypher, a peer graph query language.
SQL, the relational alternative.
ProtoBuf, the binary wire format behind gRPC.
Communication, REST vs. RPC vs. GraphQL trade-offs at the architecture level.