Web & Backend
GraphQL
GraphQL exposes a single endpoint backed by a typed schema describing every object and field a client is allowed to ask for. Instead of the server deciding a fixed response shape per URL, the client sends a query naming exactly the fields it needs, possibly across several related objects, and the server's resolvers fetch and assemble just that data. The response mirrors the shape of the query, not a fixed shape decided ahead of time.
Why it matters
- It solves over-fetching and under-fetching for genuinely nested data
- A mobile screen that needs a user's name, their last three orders, and each order's item count can get all of it in one request instead of several REST calls.
- The schema is a real, enforced contract between client and server
- A client can validate a query against the schema before ever sending it, and tools can generate typed client code automatically from that schema.
- It supports multiple client types with different data needs from one API
- A web app and a mobile app can each ask for different fields from the same underlying types without the server needing separate endpoints for each.
- Introspection makes the API self-documenting
- A client can query the schema itself, which is what powers tools like GraphQL Playground and automatic API documentation.
A schema, a query, and a matching response
Every field in a GraphQL query is resolved against a schema that declares its type. A client asks for exactly the fields it wants; a field it doesn't request never appears in the response, and a field that doesn't exist in the schema is rejected before the query even runs. Because a GraphQL server is usually one endpoint, a query is sent as the body of a single HTTP POST.
POST /graphql HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"query": "query { order(id: 42) { id status total } }"}
HTTP/1.1 200 OK
Content-Type: application/json
{"data": {"order": {"id": 42, "status": "shipped", "total": 58.5}}}Resolvers and the N+1 problem
Each field in the schema is backed by a resolver function that knows how to fetch it. That flexibility has a cost: a naive resolver for 'each order's customer' that runs one database query per order in a list produces N+1 queries — one for the list, then one more per item — even though the client only sent a single GraphQL query. Solving this usually means batching and caching those lookups within a request, commonly with a pattern called a dataloader.
Where GraphQL fits next to REST
GraphQL earns its complexity when clients genuinely need flexible, nested data and REST would mean either bloated fixed responses or many round trips to assemble the same thing. It's a heavier lift for simple CRUD services — one endpoint, one shape, one client — where REST's simplicity has no real downside to trade away.
Mistakes people make here
- Assuming GraphQL is automatically faster than REST
- A GraphQL server still has to fetch the underlying data from a database or another service; a poorly written resolver can produce the exact N+1 query problem REST endpoints are also capable of, just hidden behind a single request.
- Adopting GraphQL for a simple, stable CRUD API
- The schema, resolver, and tooling overhead is real cost that pays off when clients need flexible queries — for a service with one client and fixed data needs, REST usually gets the same job done with less machinery.
- Not limiting query depth or complexity
- Because a client can nest fields arbitrarily (a user's friends' friends' posts' comments...), an unrestricted GraphQL API is exposed to expensive or even denial-of-service-style queries unless the server enforces depth or cost limits.
- Treating GraphQL as a database
- It's a query language for an API layer, not a storage engine — the data still lives in whatever database or service the resolvers call out to, and GraphQL adds nothing to how that data is stored or indexed.
Strengths and trade-offs
Where it is strong
- Clients fetch exactly the fields they need, cutting both over-fetching and under-fetching for nested data.
- A strongly typed schema is a real, checkable contract, and it powers code generation and editor tooling.
- One endpoint and one evolving schema, rather than a growing set of REST routes and versions.
- Introspection makes the API self-documenting and explorable without separate hand-written docs.
The trade-offs
- Real added server-side complexity: resolvers, schema design, and N+1 mitigation don't come for free.
- Standard HTTP caching, which relies on distinct cacheable URLs, doesn't apply cleanly to a single POST endpoint — caching has to be handled at the application or query level instead.
- Deeply nested queries can be abused to make the server do far more work than a client's request size suggests, so cost limiting is a real operational concern.
- The learning curve — schema design, resolvers, client libraries — is steeper than a REST endpoint for a team unfamiliar with it.
Who needs this
Most valuable for teams serving multiple client types (web, mobile, third-party) with different, evolving data needs from shared underlying resources; overkill for a small, single-client CRUD service.
Questions about graphql
- Is GraphQL a replacement for REST?
- No, both remain common; GraphQL solves a specific problem (flexible, nested data needs across varied clients) and many teams use it alongside REST endpoints rather than instead of them entirely.
- Is GraphQL a database?
- No. It's a query language and runtime that sits in front of whatever actually stores the data — a SQL database, a document store, or another API — and its resolvers do the real fetching.
- Does GraphQL always perform better than REST?
- Not automatically. It can reduce round trips for nested data, but a badly written resolver can be just as slow as, or slower than, an equivalent REST endpoint, since the underlying data-fetching cost doesn't disappear.
- Can a GraphQL API be cached like a REST API?
- Not out of the box the same way. Since most GraphQL traffic goes through one POST endpoint, standard URL-based HTTP caching doesn't apply directly, and caching has to be implemented at the client or application layer instead.