# GraphQL

> GraphQL is a typed API query language and execution model that lets clients request specific fields, including related data, in one operation. In system design, it can simplify APIs for different client screens, but it moves complexity into server-side data fetching, authorization, caching, and resource limits.

Source: https://sysround.com/concepts/graphql
Updated: 2026-11-05


## How graphql works

The server publishes a schema defining types, fields, arguments, and entry points for reads and writes. For example, a Product type might expose id, title, price, and seller. GraphQL is an API layer, not a database: those fields can come from SQL, caches, or downstream services.

A client sends an operation such as `query { product(id: "p1") { title seller { name } } }`. The server parses and validates it against the schema, then executes field resolvers to produce a response with the requested shape. Clients choose exposed fields, not arbitrary database queries.

Resolvers fetch or compute field values. A product resolver might call a catalog service, while the seller resolver calls a user service. Independent fields may execute concurrently, but nested dependencies still introduce latency, so one client request can hide many backend calls.

Batch related lookups to avoid the N+1 problem. If a page returns 20 products, resolving each seller separately can add 20 calls after the product lookup. A request-scoped loader can collect seller IDs, deduplicate them, and issue a bulk lookup when the downstream API supports it.

Queries read data; mutations request changes; subscriptions support ongoing results through an additional delivery mechanism. Top-level mutation fields execute serially within an operation, but that does not create a database transaction. Execution errors can return partial data alongside errors, with nulls propagating according to schema nullability.

## Worked example: A product page with bounded backend fan-out

Suppose a mobile screen requests 20 products with each product's title, price, and seller name. A naive resolver implementation makes one catalog call plus 20 seller calls: 21 backend calls for one GraphQL operation. A request-scoped loader collects the seller IDs and uses a bulk user lookup, reducing this path to two calls if both services support those operations. The schema caps the page at 50 products, and query-cost checks account for requested list sizes. Seller fields enforce visibility rules, and private results are not placed in a shared public cache. If the user service times out, nullable seller fields can return null with errors while product data remains available.

## Trade-offs

- **Flexible responses versus variable server work.** Clients can avoid unwanted fields and combine related reads, but nested lists can multiply backend work unless page sizes and query cost are bounded.
- **Fewer client round trips versus backend fan-out.** One operation can replace several client requests while still requiring many service calls, so batching, deadlines, and resolver tracing remain necessary.
- **Typed contracts versus schema maintenance.** Validation and generated client types help catch mistakes, but field deprecation, ownership, nullability changes, and compatibility still require coordination.
- **Flexible queries versus simpler shared caching.** Response shapes and authorization contexts complicate shared response caching, although persisted queries over GET can support HTTP caching when responses are safe to share.
- **Partial results versus more client error handling.** Returning usable fields when one dependency fails can improve resilience, but clients must handle errors and nulls rather than treating HTTP success as complete success.
- **A unified schema versus operational coupling.** A schema spanning multiple services can simplify clients, but slow dependencies and schema changes require clear ownership, isolation, and observability.

## When to use it in a system design interview

- Web, mobile, and other clients need different field selections from the same domain, and fixed responses repeatedly overfetch or require extra requests.
- A screen combines related data from several services, and reducing client round trips is an explicit requirement.
- The interviewer asks for a client-facing aggregation layer with a discoverable, typed contract.
- API consumers need flexible reads, and the team can operate query-cost controls, resolver-level authorization, and batching.

## How to bring it up in the interview

- Start with the client requirement, not the technology: explain which screen needs which data and compare GraphQL with a REST endpoint tailored to that screen.
- Walk through one operation down to its backend calls. Interviewers often probe whether a single request hides N+1 queries, serial dependencies, or excessive fan-out.
- Define resource controls explicitly: maximum page sizes, estimated cost that accounts for list sizes, depth limits, per-identity budgets, and execution deadlines. Request counts alone do not capture GraphQL workload.
- Explain that authentication happens at the boundary, while authorization must protect individual objects and fields in the trusted data-access path. Request-scoped loaders and caches must preserve the caller's access context.
- State failure and consistency semantics. Explain which fields may be null, how partial errors reach clients, and why composing service reads does not produce a consistent cross-service snapshot.

## Common mistakes

- Treating GraphQL as a graph database or assuming the schema determines the physical storage model.
- Claiming that one HTTP request means one database query, lower latency, or less total backend work.
- Using depth limits alone to prevent expensive queries while ignoring broad selections, aliases, and large nested lists.
- Assuming GraphQL automatically provides authorization, transactional mutations, or cache invalidation.
- Adding GraphQL to a simple fixed API without showing a client need that justifies its operational costs.

## FAQ

### What is GraphQL in system design?

GraphQL is a typed API layer where clients select fields from a server-defined schema. It often sits between clients and backend services or databases, composing their results into the requested response. It does not replace storage, service boundaries, or consistency decisions.

### Is GraphQL better than REST?

GraphQL is useful when clients need different combinations of related data and client round trips are costly. REST is often simpler for stable resource operations and straightforward HTTP caching. A tailored REST aggregation endpoint can solve many of the same screen-level problems, so compare complexity rather than declaring a universal winner.

### How do you scale a GraphQL API?

Scale stateless API instances horizontally, but first control the work each operation can trigger. Use bounded pagination, batching, appropriate caches, cost-aware admission controls, and deadlines. Trace operations and downstream calls because adding API instances does not fix an overloaded database or excessive resolver fan-out.

## Related concepts

- [REST API design](https://sysround.com/concepts/rest-api-design)
- [API gateway](https://sysround.com/concepts/api-gateway)
- [Pagination](https://sysround.com/concepts/pagination)
- [Caching strategies](https://sysround.com/concepts/caching-strategies)


---
SysRound (https://sysround.com) is an AI practice tool for system design interviews: a voice or typed AI interviewer, a shared whiteboard, then feedback on every skill and a model answer. Its feedback is AI-generated and can be wrong.
