GraphQL is a query language for APIs. The client sends a query describing the exact shape of data it wants; the server returns that shape, no more, no less. One endpoint, a strongly typed schema, deeply nested data in a single round-trip — and a fresh set of problems to manage.
← Back to APIs & NetworkingPOST /graphql. The body is the query.query (read), mutation (write), subscription (live updates, usually over WebSocket).The mobile screen needs user.name, the user's last 3 orders, and each order's total. In REST: 1 + 1 + 3 round-trips, plus a lot of fields you didn't want. In GraphQL: one round-trip, exactly those fields.
query {
user(id: 42) {
name
orders(last: 3) {
total
}
}
}
A typed contract that web, iOS, and Android can all share. Each client asks only for the fields it needs from the same backend. Removes the temptation to ship one BFF endpoint per screen.
The GraphQL server can call several REST APIs, a couple of databases, and a search engine, then return one consolidated tree. The client doesn't see the orchestration — and the orchestration logic stays in one place rather than smeared across mobile and web codebases.
Introspection means the schema is queryable. GraphiQL gives you autocomplete and inline docs in the browser. Codegen generates type-safe TypeScript hooks (useGetUserQuery) so the wire shape and the React component's types are guaranteed to match.
Everything is POST /graphql with a different body. CDN and browser HTTP caches see one URL — they can't help. Solutions: persisted queries (server stores the query, client sends a hash, request becomes cacheable GET), client-side normalized caches (Apollo, Relay, urql), or bolt on Automatic Persisted Queries to recover REST-style edge caching.
A query for "10 users and their orders" calls the user resolver once, then the order resolver 10 times — naive resolvers turn one query into dozens of database hits. The fix is the DataLoader pattern: batch and de-duplicate within a single request. Every serious GraphQL backend uses it; forgetting it is how prod databases fall over.
A client can request deeply nested fields, or 100,000 users at once. Defenses: query depth limits, complexity scoring, persisted-only queries in production (clients can only run pre-registered queries), per-operation rate limits.
graphql-multipart-request-spec or pre-signed URLs to S3 instead.200 OK with errors in the body. HTTP-level monitoring lies; instrument GraphQL errors separately.401, 403, 429, etc. Auth and rate limits live in resolvers and middleware.Real-time GraphQL runs over WebSocket (or SSE via graphql-sse). You inherit all the WebSocket ops complexity — connection lifecycle, sticky sessions, multi-node fan-out via Redis. Often simpler to use SSE or a dedicated pub/sub service for real-time and keep GraphQL synchronous.
Apollo Federation (and the open GraphQL Federation spec) lets multiple backend services own different parts of one unified schema. The gateway stitches sub-graphs together — Users service owns the User type, Orders service owns Order and extends User with an orders field. Clients see one graph; teams own slices.
It's the natural endpoint of "GraphQL as the API layer over many microservices." Comes with operational weight — schema composition, change management, graph governance — that pays off only at scale.
Skip GraphQL for: simple public APIs (REST is friendlier to outsiders), file-heavy workloads, anything where CDN caching matters more than client-flexibility, and small teams without the bandwidth to operate DataLoader, persisted queries, and complexity limits properly.