GraphQL Interview Questions

Commonly asked GraphQL interview questions with clear, practical answers.

Common GraphQL interview questions covering the core trade-offs versus REST and the practical issues that come up building a real GraphQL API.

Q: What are the main trade-offs between GraphQL and REST? GraphQL lets clients request exactly the fields and nested relationships they need in a single request, avoiding the over-fetching and under-fetching common with fixed REST endpoints — valuable when multiple clients (web, mobile, third parties) have different data needs from the same backend. The trade-offs: a steeper learning curve (a schema and query language to design and maintain), harder HTTP-level caching (every request goes to the same URL, so you lose free URL-based caching), and new operational concerns like the N+1 query problem and query complexity limits that a fixed REST endpoint doesn't have to think about.

Q: What is over-fetching, and what is under-fetching? Over-fetching is when an API response includes more fields than the client actually needs — wasted bandwidth and parsing, common with general-purpose REST endpoints designed to serve many different clients. Under-fetching is the opposite problem: a single endpoint doesn't return enough related data, forcing the client to make multiple sequential requests to assemble what it needs (e.g., fetching a post, then separately fetching its author). GraphQL addresses both by letting the client's query itself define the exact response shape, including nested relationships, in one round-trip.

Q: What is the N+1 query problem, and how is it typically solved? It happens when resolving a field on each item in a list of N results (like each post's author) triggers a separate database query per item, instead of one batched query — 1 query for the list plus N queries for the related field. It's typically solved with DataLoader, which batches all the individual key lookups requested within the same event-loop tick into a single query (e.g., WHERE id IN (...)), and caches results within a request so the same key isn't fetched twice.

Q: What's the difference between a query and a mutation in GraphQL? Both use nearly identical syntax with a selection set choosing which fields to return, but a query is conventionally expected to be read-only and side-effect-free, while a mutation signals that the operation changes data. A practical difference: when a request contains multiple mutations, the server executes them serially, one after another, whereas independent query fields can be resolved in parallel — because a later mutation might depend on an earlier one's effect.

Q: What is a resolver, and what four arguments does it typically receive? A resolver is the function responsible for producing the actual data for one specific field in the schema. It typically receives parent (the already-resolved value of the parent field), args (the arguments passed to this field in the query), context (a shared object built per request, usually holding things like a database connection or the authenticated user), and info (metadata about the query itself, rarely used directly).

Q: Why can't GraphQL responses typically be cached with plain HTTP caching the way REST responses can? Because REST's caching model relies on the request URL (and method) identifying a specific, cacheable resource — GET requests to the same URL return the same shape of data. GraphQL, by contrast, sends nearly every request to the same single endpoint via POST, with the actual query and variables in the body, so there's no stable URL a generic HTTP cache can key on. GraphQL clients (like Apollo Client or Relay) instead implement their own normalized, field-level caching on the client side to compensate.

Q: How would you stop a malicious client from sending a deeply nested, resource-exhausting GraphQL query? Two complementary defenses: a maximum query depth check rejects queries nested beyond a reasonable limit before execution, and a cost/complexity analysis assigns each field a "cost" (often scaled by arguments like limit) and rejects any query whose total calculated cost exceeds a budget. Depth limiting alone misses a shallow but wide attack — like requesting the same expensive field dozens of times under different aliases in one request — which is exactly what complexity-based limiting is meant to catch as well.

Q: Why is rate limiting a GraphQL API by request count alone usually insufficient? Because unlike a REST endpoint where each call does roughly known, fixed work, a single GraphQL request can be nearly free ({ __typename }) or extremely expensive (a deeply nested query touching a large fraction of the dataset) — treating every request as "one unit" either lets a handful of enormous queries do outsized damage, or unfairly throttles legitimate clients making many small ones. Rate limiting by calculated query cost (deducted from a rolling per-client budget) targets the actual resource consumption instead of just the number of HTTP calls.

Q: What is GraphQL Federation, and what problem does it solve that a single monolithic schema doesn't? Federation lets multiple independently-owned services each expose their own piece of a schema — including declaring shared "entities" via directives like @key — which a gateway composes into one combined graph for clients to query, automatically planning which subgraph(s) to call for any given request. It solves the organizational bottleneck of a single team owning one enormous schema as a company grows to many teams and services, letting each team evolve and deploy its own subgraph independently while clients still see one unified API.

Q: What are persisted queries, and how do they help both performance and caching? A persisted query is a query that's been pre-registered with the server and identified by a short hash instead of being sent as full query text on every request — the client sends just the hash (and its variables), shrinking request payloads considerably. Because the hash makes the query's identity stable and compact enough to fit in a URL, persisted queries can also be sent as plain HTTP GET requests, which is what actually makes ordinary CDN/browser HTTP caching possible for GraphQL — something a typical POST-with-a-body GraphQL call can never get on its own.