Limited-time offer: 80% off every plan

See plans

GraphQL Interview Questions: Defend Your Design Choices

GraphQL interview questions rarely stop at definitions. Learn a repeatable way to explain schema, resolver, pagination and caching trade-offs out loud when the interviewer asks 'why not the other way?'

By Emily Miller14 min read

Most GraphQL interview questions do not stop at "what is a resolver?" Anyone can memorise a definition the night before. The part that decides the interview is the follow-up: "Why did you make that field nullable?" "Why not just return a JSON blob?" "When would you not batch?" This article is about that second layer — the moment an interviewer pushes on a decision you made and waits to see how you think. It is not a glossary. It is a way to structure your reasoning so you can defend a schema, resolver, pagination or caching choice honestly, including its costs.

The core habit is simple. For every design decision you mention, be ready to say four things: what you chose, why you chose it, what it costs, and what you would revisit. That sentence shape works whether you built the server yourself or only consumed the API.

Why GraphQL Interviews Move Quickly Past Definitions

Definitions are table stakes. Schema, query, mutation and resolver are the vocabulary, not the signal. Interviewers assume you know them, so they spend their time on judgement.

The real signal is whether you can explain why a design choice was made and what it costs. A candidate who says "I used cursor pagination because it is best practice" has said nothing. A candidate who says "I used cursor pagination because offset pagination shifted rows when new records arrived, and the cost was a more complex backend and no cheap total count" has shown they were in the room when the decision was made.

Frame every answer as decision, trade-off, consequence, and what you would revisit. If you cannot name a cost, the interviewer will assume you copied the pattern without understanding it. Naming the cost is what makes the answer credible.

One more expectation to set: you will not always have built the thing. That is fine. Honest answers about what you observed, read or inherited land better than invented architecture.

Schema Design: Naming, Nullability and Shape

Nullability is a contract, not a style choice

When you mark a field non-null, you are promising clients it will always have a value. That promise shapes error handling: if a non-null field fails, the error can propagate upward and null out the parent object. If the field is nullable, the failure stays local and the rest of the response survives. Say which behaviour you wanted and why.

A useful script: "I made email non-null on User because every user has one and clients should not write defensive checks. The cost is that a data problem in one user can null the whole user object, so we treat missing email as a data-integrity bug rather than a normal state."

Object versus scalar, and when a custom scalar earns its place

Returning an object gives clients room to grow — you can add fields later without breaking anything. Returning a scalar is smaller and simpler now. Custom scalars, such as a date or an identifier with a format, are worth the complexity when validation and serialisation rules need to live in one place. They are not worth it when a plain string with a documented format would do.

Naming consistency pays off in generated types

If some fields are createdAt and others are created_at, every generated client type looks slightly different and every developer hesitates. Pick a convention, apply it everywhere, and mention that you did so deliberately. Consistency is a small decision with a large downstream effect on code generation and autocomplete.

Follow-up to expect: "Why not just return a JSON blob here?"

Answer with the client contract. A typed object tells clients exactly which fields exist, lets tooling validate queries before they run, and lets you evolve fields individually. A JSON blob pushes validation to the client, hides the shape from tooling, and makes it hard to deprecate anything. The trade-off: a blob is faster to ship when the shape is genuinely open-ended, such as arbitrary metadata.

Resolvers and the N+1 Problem

How a naive resolver multiplies database calls

Suppose you fetch a list of twenty posts, and each post has an author field resolved separately. A naive resolver that looks up the author per post produces twenty-one database calls where one or two would do. That is the N+1 problem: one query for the list, then N more for the related data.

Batching and caching in plain terms

Batching means collecting the author lookups requested during a single tick of execution and issuing one query for all of them. Caching means remembering the result for the duration of the request so a repeated lookup does not hit the database again. DataLoader is the common name for a helper that does both, but you can describe the idea without the name: batch within a tick, cache per request, key by identifier.

Be honest about the added complexity

Batching adds machinery. Cache invalidation becomes a question you have to answer, and the cache must be scoped per request — a cache that leaks across requests can serve one user's data to another. Say that out loud. Interviewers respect a candidate who names the operational cost of their own optimisation.

Follow-up to expect: "When would you not batch?"

When the query is shallow and the fan-out is low. If a resolver returns a field that is already loaded, or a list that is small and rarely nested, batching adds indirection for no measurable gain. Saying "I would measure first" is a legitimate answer.

Pagination, Filtering and Connection Patterns

Offset versus cursor pagination

Offset pagination is easy to explain: skip twenty, take ten. Its weakness is stability. If rows are inserted or deleted while a client pages through, items shift and clients see duplicates or miss records. Cursor pagination anchors to a position in a stable order, so the next page starts after a known point.

Why cursor pagination is common, and what it demands

The connection pattern — edges, nodes, page info, cursors — is common in GraphQL APIs because it gives clients a stable way to page and a clear place to ask for more. It demands a stable sort key from the backend, usually an indexed column, and a cursor that encodes that position reliably. If your sort key is not unique, you need a tiebreaker, and you should mention that.

Filtering, sorting and their effect on caching

Every filter and sort combination is a different result. That makes caching harder, because two requests that look similar produce different answers. If you cache, key on the full argument set, and be ready to explain why a filter that is not indexed is a performance risk rather than a feature.

Follow-up to expect: "How do you handle total counts?"

Counts are expensive because they often require scanning more than the page itself. Options include returning an approximate count, returning a count only when the client asks for it via a separate field, or omitting it and letting the client page until it runs out. Ask whether the interface truly needs a number, or whether "more results" is enough.

Caching, Persisted Queries and Performance

Three layers of caching, not one

HTTP-level caching sits between the client and your server. Resolver-level caching sits inside a request, as with batching. Client-level caching, such as a normalised store, sits in the application. These are different tools with different lifetimes, and conflating them is a common interview stumble.

Why POST complicates CDN caching

GraphQL requests are often sent as POST, and CDNs traditionally cache GET responses by URL. A POST to a single endpoint looks like one URL to the cache, so responses are not cached the way REST endpoints are. Teams work around this with persisted queries, which turn a query into an identifier that can be sent as a GET, or with a cache key derived from the query and variables.

Persisted queries and complexity limits as operational tools

A persisted query is a query the server already knows, referenced by a hash or ID. That reduces payload size and lets you allowlist what the server will run. Complexity limits cap how much work a single query may request. Neither is a buzzword: both are ways to keep a public endpoint predictable.

Follow-up to expect: "How would you stop an expensive query?"

Depth limits stop deeply nested queries. Cost analysis assigns a weight to fields and rejects queries above a budget. Timeouts are the backstop when analysis misses something. Say which layer you would reach for first and why.

Errors, Partial Responses and Nullability in Practice

Data and errors can arrive together

A GraphQL response can contain both data and an errors array. REST-trained clients often expect one or the other, so this surprises them. Explaining why it happens — field-level failures do not have to fail the whole request — shows you understand the model.

Transport errors versus field-level errors

A dropped connection or a malformed request is a transport error. A resolver that throws because a record is missing is a field-level error. The first is handled by the client's network layer; the second is handled by the component that asked for the field. Keep the distinction clear.

How nullability affects propagation

If a failing field is non-null, the error moves up to the nearest nullable parent, which may be nulled as a result. If the field is nullable, the failure stays where it happened. This is the practical link between the schema decision you made earlier and the error behaviour a client sees.

Follow-up to expect: "How do you make errors useful to clients?"

Use stable error codes, put machine-readable detail in the extensions object, and document what each code means. "Something went wrong" is not an error contract.

GraphQL Versus REST: Answering Without Taking Sides

The honest comparison is about fit, not superiority. GraphQL lets clients ask for the fields they need, which reduces over-fetching and under-fetching, but it can move complexity to the server and make caching harder. REST is easy to cache at the HTTP layer and simple to reason about, but clients often make several round trips or receive more data than they use. gRPC is a strong fit for internal service-to-service calls where contracts are strict and performance matters.

The right answer depends on clients, team size, caching needs and existing infrastructure. A mobile client with varied screens benefits from asking for exactly what it renders. A small team with a handful of stable endpoints may not need a new layer at all.

Follow-up to expect: "Would you migrate everything?"

No. Explain incremental adoption: wrap existing services, expose GraphQL alongside REST, and move one consumer at a time. That is the strangler pattern in practice, and it keeps the risk contained.

Security, Authorization and Abuse Prevention

Field-level authorization

Authorization belongs where the data is resolved, not only at the gateway. A resolver-level check ensures that a field is protected even when it is reached through a different query path. Mention that you keep the checks close to the data and consistent across resolvers.

Complexity and depth limits as protection

A public GraphQL endpoint can be asked for a deeply nested, expensive query by anyone. Depth limits and cost analysis are how you bound that. They are protection, not optimisation.

Introspection in production

Some teams disable introspection in production to reduce the exposed surface; others keep it on for tooling and rely on authorization instead. Both positions are defensible. Say which you chose and why rather than declaring one correct.

Follow-up to expect: "Where do you enforce permissions?"

Compare the options: schema directives keep rules visible in the schema; resolver logic keeps them close to the data; a gateway centralises them but can miss field-level nuance. The useful answer names the trade-off between visibility and proximity.

How to Practise GraphQL Follow-Ups Out Loud

Reading about trade-offs is not the same as saying them under pressure. Here is a routine you can run this week.

  1. Pick five design decisions from a project you know: a nullability choice, a pagination style, a caching approach, an error format, an authorization check.
  2. For each one, write a single sentence in the shape "I chose X because Y, and the cost is Z."
  3. Add the fourth beat: what you would revisit, and under what condition.
  4. Say each answer out loud, standing up, without notes. Aim for thirty to sixty seconds.
  5. Have a partner interrupt with "why not the other way?" after each answer, and respond without restarting your sentence.
  6. Record yourself once and listen for filler, hedging and any claim you cannot back up.

Review your answers for honesty as you go. Describe what you actually built, not an idealised version. If you inherited a design you disagreed with, that is a strong answer: explain what you would change and why you did not change it at the time.

If you have only used GraphQL as a consumer, say so and pivot to what you observed. You can still discuss why a field was nullable, why a query felt slow, or why the API used cursors. For more on structuring technical answers around concepts and trade-offs, see our article on Python Interview Questions: Concepts and Trade-Offs, and browse our blog for related practice guides.

Simulating follow-up pressure with a partner or a practice session builds the reflex of naming a cost before you are asked for it. The goal is not a perfect answer; it is an answer you can stand behind while someone pushes on it.

Before Your Next Interview

Write the cost sentence for three decisions you made. If you can say what each choice gave up, you are ready for the follow-up. Practise saying them out loud once more the night before, and keep the fourth beat — what you would revisit — ready for the question that comes after.

Frequently asked questions

What GraphQL interview questions come up most often for backend roles?

Expect questions that start from a decision rather than a definition: why a field is nullable, how you avoid N+1 queries, which pagination style you chose and why, how caching works across HTTP, resolver and client layers, and how you enforce authorization. Definitions may appear, but the follow-up is where the interview is decided.

How do I answer 'why GraphQL instead of REST' without sounding like I am reciting a blog post?

Anchor the answer in your own context. Name the clients you had, the over-fetching or round-trip problem you were solving, and the cost you accepted, such as harder HTTP caching or added server complexity. Then say when REST or gRPC would have been the better fit. Specific context reads as experience; general benefits read as a recitation.

Do I need to know DataLoader by name to explain the N+1 problem?

No. The idea matters more than the library name: collect lookups requested during one execution tick, issue a single query for them, and cache results for the duration of the request. If you can describe batching, per-request caching and the invalidation cost, you have answered the question.

How deep should I go into schema design in a 45-minute interview?

Go as deep as the interviewer pushes, and no deeper. Start with the decision and its cost in one or two sentences, then wait. If they ask a follow-up, expand. Volunteering a long design history eats time you may need for resolvers, pagination and security later in the session.

What should I say if I have only used GraphQL as a consumer, not built a server?

Say that plainly, then talk about what you observed. You can still discuss why a field was nullable, why a query felt slow, how pagination behaved when data changed, or how errors surfaced in the client. Honest observation is more convincing than an invented architecture, and it shows you can reason about the decisions behind the API.

Are GraphQL subscriptions commonly asked about in interviews?

They come up, but usually as a follow-up rather than the main topic, and often in roles with real-time features. Be ready to explain what they are for, how they differ from queries and mutations, and the operational cost of holding connections open. If you have not used them, say so and describe how you would evaluate whether the use case needs them.

Practise answering follow-up questions out loud before your next interview.