40 GraphQL interview questions with detailed answers, sorted from basic to advanced. Free to practice, with progress saved automatically.
GraphQL is a query language and runtime for APIs in which the client specifies exactly which fields it needs, unlike the fixed responses of a typical REST endpoint. Topics include how it differs from REST, schema design and nullability, variables, mutation input and payloads, partial errors alongside data, authorization, performance, normalized client caching, and persisted queries.
and 14 more topics.
GraphQL is a query language for APIs where the client describes exactly the data it needs and the server returns that shape. In REST each endpoint has a fixed response shape, so clients either over-fetch or make several calls and stitch results together. The two…
The schema is a contract describing every type, field and argument a client can query. Both sides rely on it, so wrong field names or types are rejected before execution, and code generators, editor autocomplete and generated documentation all derive from it. Each type describes…
Query reads, mutation writes, subscription is a server-pushed stream. Queries and mutations are a single request/response, usually sent over HTTP POST. Subscriptions differ: the client subscribes once, the server keeps a connection open (usually WebSocket, sometimes Server-Sent Events) and pushes a new result whenever the…
A resolver is a function that returns the value for one field. The server walks the query shape and calls the matching resolver per field, so a deeply nested query triggers a whole tree of calls. Each resolver receives four things: the parent field value;…
It means the field never returns null. That is a strong commitment: if the resolver returns null, the error propagates up to the nearest nullable parent, potentially wiping out a large branch of data. This propagation is why many teams adopt a mostly nullable strategy.…
With variables the query document keeps a stable shape across values, so the server can parse and cache it while values are type-checked against the schema. Interpolation produces a different query every time and lets user input influence query structure. A stable shape also enables…
A fragment groups a reusable set of fields. In practice it lets each UI component declare the data it needs, with the screen composing fragments into one query. This matters as an app grows: editing a component updates the query automatically and nobody must remember…
Yes. A response has a data part and an errors part, so a query can return partial results: failing branches become null and are recorded in errors while other branches still carry data. A client consequence: HTTP status alone cannot indicate success, since it is…
Querying 50 posts with authors produces 51 database calls: one for the list, then the author resolver runs separately for each post. The standard fix is DataLoader: it defers individual load calls within one event loop tick, collects them into a key list, and calls…
It identifies objects by type name plus record identifier. The same object appearing in several queries is stored once, so updating it in one place is reflected on every visible screen. The requirement: the schema must expose an identifier and the client must request it.…
A cursor points at a definite position in the result set, so newly inserted records do not shift pages as with offset pagination. Say you load 3 posts per page, newest first: after page 1 is loaded, a new post is published at the top.…
Put it in the business layer behind resolvers, where both the user and the specific record are known. A gateway check alone is insufficient because it knows who the user is but not whether they may see the requested record. What makes it harder than…
Because clients choose the query shape, a deeply nested query through bidirectional relations can grow exponentially and overload the server with a single request. The classic example: a post has an author, an author has posts, each post has an author. Ten levels is a…
Automatic persisted queries let the server learn a document on first sight, after which the client sends only a hash. The main goal is reducing upload size. An allowlist means the server accepts only queries registered at build time and rejects everything else. That is…
Return the changed record with related fields so the client updates its cache and UI immediately. With a normalized cache (a cache that stores each object once by id, with every query pointing at that copy), the identifier plus the changed fields is enough for…
GraphQL evolves by adding new fields and deprecating old ones. Old clients keep working because the old field remains, and it is removed only when nobody queries it. The condition for this to work is knowing who uses which field. Without per-field usage data nobody…
Because everything goes through one endpoint (usually /graphql), typically with POST, intermediate caches such as a CDN or the browser cannot distinguish queries. In exchange, GraphQL moves caching to the client with a normalized cache: it stores each object once by id (for example User:5),…
It produces exact types per query, so wrong field names or types fail at compile time. Combined with schema checks in CI, a breaking change fails the build rather than the users application. The usual workflow: read the schema from a file committed in the…
Subscriptions suit unpredictable events needing immediate push: new messages, price changes, order status. For data changing on a regular cadence, polling is usually simpler and much cheaper. The operational cost is commonly underestimated: each client holds a long-lived connection so the server carries connection load,…
An interface requires every implementing type to share a field set, so those fields can be queried without knowing the concrete type. A union groups types with nothing in common, so per-type fragments are required. Choose by semantics: use an interface when types genuinely share…
Custom scalars allow format validation and normalization at the schema layer: invalid values are rejected before reaching resolvers and every client receives one format. Common ones: date and time, email, URL, precise decimals for money. The trap is a free-form JSON scalar. It is convenient…
They toggle part of a query based on a variable: @include(if: $x) fetches a field only when $x is true, @skip(if: $x) drops the field when $x is true. So one document serves several cases, which matters for persisted queries because the number of distinct…
The schema handles types and requiredness, while business rules live in the business layer. The type system cannot express rules such as an amount being positive, a coupon still being valid, or an end date following a start date. How errors are returned is also…
Base it on estimated query cost, not request count. A query fetching one record and a three-level nested query over thousand-item lists differ by orders of magnitude in work. A common cost model: assign points per field, multiply by record counts implied by pagination arguments,…
Schema-first: write the schema in the definition language then implement resolvers against it. Code-first: define types in code and generate the schema. The real difference is where the contract source of truth lives and how teams collaborate. Schema-first suits contracts negotiated between client and server…
Keep it in application state (for example useState, Zustand or Redux) rather than the GraphQL client cache. The cache is designed for server data: it refetches and evicts stale copies on expiry or after a write, while UI state has a completely different lifecycle. The…
Measure by operation name and by per-field resolution time. Endpoint-level metrics are meaningless since everything shares one URL, and the status code is usually 200 even when the response contains errors. Metrics worth tracking: execution time per named operation at high percentiles; data source calls…
Run real queries through the schema and assert the results, since that covers the schema, resolvers and business layer exactly as a client experiences them. Fake the underlying data sources for speed and stability. Cases worth covering beyond the happy path: an error in one…
The most robust approach: request a pre-signed URL through a mutation, upload directly to storage, then call a second mutation to attach the file to a record. The file never passes through the API server, avoiding size limits and heavy load. The GraphQL specification does…
Use GET together with persisted queries: the URL becomes unique per hash and variables, so CDNs and intermediate caches can cache it. For long inline queries, POST is safer because URL length limits do not apply. This is one of the few ways to regain…