GraphQL
The model
- The schema is the contract. It gives every field a type, and the server checks each query against it before running it.
- The client names the fields it wants. The response comes back in the same shape as the query.
- Every field has a resolver, the function that returns its value. That value becomes the
parentof the fields under it. - A failed field becomes
nulland adds an entry toerrors. The rest of the data still arrives, usually with HTTP200. - Every operation goes to one endpoint, usually as a
POST. That is why HTTP caching takes more work than in REST.
GraphQL vs REST
what it fixes, what it costs, and how to move overRequests over HTTP
POST /graphqlA JSON body with query, variables and operationName.GET, with the query in the URL. Mutations must use POST.200, with data and errors side by side.TrapMonitoring that reads only status codes misses most GraphQL errors. Read the body too.
Asked in interviews
AskedHow does GraphQL work over HTTP?GraphQL's downsides
Asked in interviews
AskedWhat are the limitations of GraphQL?GraphQL or gRPC
TipMany systems use both, with gRPC between services and GraphQL in front of the apps.
Asked in interviews
AskedWhat is the difference between GraphQL and gRPC?Move from REST to GraphQL
class UserAPI extends RESTDataSourceResolvers call the existing REST endpoints.TrapResolvers that call REST once per item bring the N+1 problem back. Batch them with DataLoader where REST has a bulk endpoint.
Asked in interviews
AskedHow do you migrate from REST to GraphQL?Schema & types
the typed contract the server checks every query againstTypes, fields and null
type User { id: ID! }name: String!Fields are nullable unless their type ends in !.posts: [Post!]!type Query { me: User }Scalars, enums and inputs
Int, Float, String, Boolean and ID. ID is sent as a string.scalar DateServer code parses and validates its values.enum Role { ADMIN USER }input NewUser { name: String! }TrapYou can't use an object type like User as an argument. Inputs hold only scalars, enums and other inputs, or lists of them.
Interfaces and unions
type User implements Nodeunion Result = User | PostTrapA union has no shared fields. Every field except __typename needs an inline fragment.
Schema-first or code-first
TipIn code-first, the schema exists only once the code runs. Generate the SDL to review changes.
Asked in interviews
AskedWhat is the difference between schema-first and code-first development?Change the schema safely
@deprecated(reason: "Use name")Add the new field and mark the old one. Remove it once usage tracking shows no client needs it.TrapOld mobile app versions stay installed, so they keep asking for old fields.
Asked in interviews
AskedHow do you version a GraphQL API?Queries, fragments & paging
ask for exactly the fields a screen needsQuery, mutation, subscription
query reads data, mutation changes it, and subscription gets updates the server pushes.Arguments, variables, aliases
users(role: ADMIN)Any field can take arguments, nested ones too.query GetUser($id: ID!)user(id: $id)admins: users(role: ADMIN)TrapNever paste user input into the query text. Send it in variables.
Fragments
fragment UserInfo on User...UserInfo... on Post { title }TipColocate each fragment with the component that renders its fields. Relay combines them into one query.
Directives
email @include(if: $full)posts @skip(if: $lite)@include, @skip, @deprecated and @specifiedBy, plus @oneOf for inputs that take exactly one field.Asked in interviews
AskedWhat are directives in GraphQL?Page through a list
node and its cursor, beside a pageInfo object.users(first: 10, after: $cursor)Pass the last endCursor as after.hasNextPageFetch one row more than first. If it comes back, there is a next page.Asked in interviews
AskedHow do you implement cursor-based pagination?Resolvers & Apollo Server
how the server fills in each fieldWrite a resolver
(parent, args, context, info)context. The server builds it once per request.parent.async. Start independent calls together with Promise.all.How a request runs
parent of the fields nested under it.null, and its error and path go into errors.Wrap resolvers
applyMiddleware(schema, logger)From the graphql-middleware library.mapSchema transformer.TrapSchema no longer works. GraphQL Tools 8 removed it, and Apollo Server 3 dropped schema.
Run Apollo Server
startStandaloneServer(server) expressMiddleware(server) Test the API
args and context.server.executeOperation({ query })TrapApollo Server 3 dropped apollo-server-testing and its create, which old guides still use.
Asked in interviews
AskedWhat are some GraphQL testing strategies?N+1 & caching
stop one query from turning into hundreds of database callsSpot the N+1 problem
TrapSmall development data hides it, so N+1 often shows up only in production.
Asked in interviews
AskedWhat is the N+1 query problem in GraphQL?Batch with DataLoader
new DataLoader(batchFn) It gets every key from one tick of the event loop. Return one value per key, in the same order.loaders.posts.load(user.id) context function.TrapA loader shared across requests keeps serving values cached for earlier requests.
Asked in interviews
AskedHow do you solve the N+1 problem with DataLoader?Cache GraphQL responses
@cacheControl(maxAge: 60)A response lives as long as its lowest maxAge.GET, often as a persisted query hash.TrapKeep user-specific data out of shared caches.
Asked in interviews
AskedWhat are some GraphQL caching strategies?Persisted queries
TrapAPQ blocks nothing, since any client can register any query. Only an allowlist stops attackers.
Asked in interviews
AskedWhat are persisted queries and query allowlisting?Mutations & errors
design writes, and report what failedDesign a mutation
createPost(input: CreatePostInput!) CreatePostPayload It holds the changed objects, so the client can update its view.publishPost, not one generic update for the whole record.Asked in interviews
AskedWhat is the best practice for mutation input design?Return errors
errors: [UserError!], each with a field and a code.union Result = User | EmailTakenextensions: { code: 'USER_NOT_FOUND' }Pass it to GraphQLError.formatErrorSwap database messages for a generic one, and log the real error.TrapKeep the top-level errors array for failures the client can't fix, like an outage.
Wrap writes in a transaction
SELECT ... FOR UPDATE.TrapEach top-level mutation field commits on its own. If a later one fails, the earlier one stays.
Asked in interviews
AskedHow do you handle database transactions in GraphQL?Upload files
scalar UploadFollow the multipart request spec, with graphql-upload.TrapMultipart requests skip the browser's preflight, so they need CSRF protection. Apollo Server blocks them without an Apollo-Require-Preflight header.
Asked in interviews
AskedHow do you handle file uploads in GraphQL?Auth & security
know who is calling, and cap what one query costsAuthenticate requests
context function, from the Authorization header.code: 'UNAUTHENTICATED'Throw a GraphQLError with this code. Apollo Server 4 removed AuthenticationError .TrapAuthentication only says who the user is. Each field still needs an authorization check.
Asked in interviews
AskedHow do you implement authentication in GraphQL?Authorize each field
@auth(requires: ADMIN)It needs server code to enforce it.null unless the viewer owns the record.TrapA check only at the top misses other paths to the same field. Check access on each field.
Asked in interviews
AskedHow do you implement authorization in GraphQL?Limit expensive queries
depthLimit(10)maximumCost: 1000A list multiplies its children's cost by first. So 50 users with 50 posts each count as 2,500 posts.Trapgraphql-rate-limit counts in memory by default. Use its RedisStore to share counts across servers.
Hide the schema
__schema query, which returns every type and field. Tools build docs from it.introspection: falseApollo Server 4 and later turn it off by default when NODE_ENV is production.hideSchemaDetailsFromClientErrors: trueTrapHiding the schema is only obscurity. Authorization and cost limits are the real defense.
Apollo Client
fetch, cache and update data in ReactSet up Apollo Client
new ApolloClient({ link, cache })new HttpLink({ uri: '/graphql' })useQuery(GET_USER) useMutation(ADD_TODO) TrapApollo Client 4 removed the constructor's uri option and moved the hooks to @apollo/.
Asked in interviews
AskedWhat is Apollo Client?The normalized cache
User:1Its __typename plus its id.fetchPolicy: 'cache-first'A query fully in the cache skips the network.update function, or refetch the list.TrapHave each mutation return the id and changed fields. Otherwise the cache can't update the object.
Asked in interviews
AskedHow does a normalized client cache like Apollo Client's work?Optimistic UI
optimisticResponse The object you expect back, with __typename and id.TrapSkip it where a false success is costly, like a payment.
Asked in interviews
AskedHow would you handle optimistic UI after a mutation?Generate types
npx graphql-codegenpreset: clientIt generates a typed graphql() function.TrapThe old apollo CLI no longer generates code. Apollo recommends GraphQL Code Generator.
Asked in interviews
AskedWhat are some popular GraphQL code generators?Subscriptions
push live updates to clientsSubscribe to events
subscription { messageAdded { id } }A subscription takes exactly one root field.Asked in interviews
AskedWhat are GraphQL subscriptions?Serve subscriptions
graphql-ws. Server-Sent Events are the main alternative.expressMiddleware beside a graphql-ws server. startStandaloneServer has no support for them.pubsub.publish('MSG', payload)withFilter(subscribe, filterFn)PubSub for one backed by Redis or Kafka.Trapsubscriptions-transport-ws is deprecated and doesn't work with graphql-ws.
Federation & stitching
build one graph from many servicesApollo Federation
type User @key(fields: "id")That shared type is an entity.__resolveReference TrapIn Federation 2, skip extend type and @external on the key. Just declare the type with @key again.
Asked in interviews
AskedWhat is GraphQL Federation?Federation or stitching
stitchSchemas({ subschemas })Nothing matches. Try a shorter word, or clear the search.
This sheet stays free. With Pro, you can:
- Every answer on this sheet. Read the full answer to all 60 questions it links, not just the 9 free ones.
- My sheet. The cards you star and the ones you miss in Quiz me, from every sheet, on one page to quiz and print.
- PDFs. Download this sheet and My sheet, laid out for A4, with every question a link to its answer.
- The rest of Pro. Every answer in the question library, and 1,000 AI credits a month.