GraphQL essentials: 50 cards on schemas, queries and resolvers
GraphQL basics: schema types, queries, mutations, subscriptions, resolvers, variables, fragments, N+1 and DataLoader, introspection and GraphQL vs REST.
Practice now
Study without an account
Scheduled with the default Askesis timings. Progress is saved in this browser.
All 50 cards
- 1What is GraphQL?A query language for APIs plus a runtime that executes queries against a typed schema; clients ask for exactly the fields they need and get that shape back
- 2How is a GraphQL API usually served over HTTP?From a single endpoint, often
/graphql, usually as aPOSTwith a JSON body holdingquery,variablesand an optionaloperationName - 3What is a GraphQL schema?The contract of the API: every type, field, argument and root operation it supports, usually written in the Schema Definition Language (SDL)
- 4Define a
Usertype with an ID, a required name and a list of postsgraphqltype User { id: ID! name: String! email: String posts: [Post!]! } - 5What does
!mean after a type?Non-null: the field never returnsnull, or, on an argument, a value must be provided - 6
[Post]vs[Post!]![Post]: the list may be null and may contain nulls.[Post!]!: the list is never null and has no null items, though it can be empty - 7The five built-in scalar types
Int(32-bit signed),Float(double precision),String,BooleanandID - 8What is the
IDscalar?A unique identifier that is serialized as a string; it accepts string or integer input and isn’t meant to be human-readable - 9What does
scalar DateTimedo?Declares a custom scalar type; the server implements how it is serialized, parsed and validated - 10The three root operation types
Queryfor reads,Mutationfor writes andSubscriptionfor real-time events - 11What does this query return?graphql
query { user(id: "1") { name } }json{ "data": { "user": { "name": "Ada" } } }The response mirrors the shape of the query. - 12What keys can a GraphQL response contain?
datawith the requested fields and, if anything failed, anerrorsarray; both appear together when a result is partialAn optionalextensionskey carries extra metadata such as tracing. - 13Why is this query invalid?graphql
{ user(id: "1") }userreturns an object type, and every selection must end in leaf fields (scalars or enums). Add a sub-selection like{ name } - 14Pass an argument with a variablegraphql
query GetUser($id: ID!) { user(id: $id) { name } }Variables travel separately as JSON, e.g.{ "id": "42" }. A default looks like$limit: Int = 10. - 15Why use variables instead of building the query string yourself?No injection or escaping bugs, the server validates their types, and the query text stays constant, so it can be cached or persisted
- 16What is
GetUserinquery GetUser { … }?The operation name: it appears in logs and tracing, and selects which operation to run when a document holds severalA single query may use the anonymous shorthand{ me { name } }. - 17Request the same field twice with different argumentsgraphql
{ thumb: avatar(size: 64) full: avatar(size: 512) }Aliases rename fields in the result so they don’t collide. - 18What is a fragment?A reusable selection of fields on a type, spread into queries with
...graphqlfragment UserCard on User { id name avatarUrl } query { me { ...UserCard } } - 19Query a field that returns a union typegraphql
{ search(text: "ada") { __typename ... on User { name } ... on Post { title } } }Inline fragments (... on Type) select fields per concrete type. - 20What does
__typenamereturn?The name of the object’s concrete type, like"User"; clients use it with unions, interfaces and cache normalization - 21Fetch a field only when a variable is truegraphql
query Profile($withAvatar: Boolean!) { me { name avatarUrl @include(if: $withAvatar) } }@skip(if: …)is the inverse. - 22How do you retire a field without breaking clients?Mark it
@deprecated(reason: "Use fullName instead"): it keeps working, while introspection and tooling warn anyone still using it - 23Define an enum typegraphql
enum Role { ADMIN EDITOR VIEWER }Enum values are serialized as their names, like"ADMIN". - 24Interface vs unionAn interface declares fields that implementing types must have, and you can query those fields directly; a union lists types with no shared fields, so you need fragments for everything but
__typename - 25Why do mutation arguments use
inputtypes?Arguments must be input types (scalars, enums orinputobjects); regular object types can’t be used as argumentsgraphqlinput CreatePostInput { title: String! body: String } - 26Write a mutation that creates a postgraphql
mutation CreatePost($input: CreatePostInput!) { createPost(input: $input) { id title } }Returning the changed object lets clients update their caches without refetching. - 27How do top-level fields in one mutation execute?Serially, in the order written, so each write finishes before the next starts; top-level query fields may resolve in parallel
- 28What is a subscription?A long-lived operation where the server pushes a result every time an event happens, usually over WebSockets (the graphql-ws protocol) or server-sent eventsgraphqlA subscription must select exactly one root field.
subscription { messageAdded(roomId: "1") { text } } - 29What is a resolver?A function that produces the value of one field; it receives
(parent, args, context, info) - 30What happens for a field with no resolver?The default resolver returns the property of the same name on the parent object (calling it if it’s a function)
- 31What belongs in the resolver
context?Per-request shared things: the authenticated user, database clients and DataLoaders. It is created fresh for every request - 32What is
userin theUser.postsresolver?jsconst resolvers = { Query: { user: (_parent, { id }, { db }) => db.users.find(id), }, User: { posts: (user, _args, { db }) => db.posts.byAuthor(user.id), }, }TheUserobject returned by the parent resolver,Query.user; resolvers run top-down, each receiving its parent’s result - 33What is the N+1 problem in GraphQL?Fetching a list of N posts with one query, then resolving each post’s
authorwith its own query: 1 + N database round trips - 34How does DataLoader fix N+1?It collects every
load(key)made in the same tick, calls your batch function once with all the keys (oneWHERE id IN (…)query) and caches results for the requestjsconst userLoader = new DataLoader(async (ids) => { const users = await db.users.findMany({ where: { id: { in: ids } } }) return ids.map((id) => users.find((u) => u.id === id) ?? null) }) // Post.author: (post) => userLoader.load(post.authorId) - 35What must a DataLoader batch function return?A promise of an array with the same length and order as the keys, holding
nullor anErrorfor keys that weren’t found - 36Why create DataLoaders per request?Their cache would otherwise outlive the request, serving stale data and leaking one user’s results to another
- 37What is introspection?Querying the schema itself through fields like
__schemaand__type; it powers GraphiQL, editor autocomplete and code generators - 38What does this query return?graphql
{ __type(name: "User") { fields { name } } }The names of all fields on theUsertype, read from the schema through introspection - 39Should introspection be on in production?Private APIs often disable it to reduce attack surface; public APIs keep it. It isn’t real security on its own, since error suggestions can still leak field names
- 40GraphQL vs REST: fetching dataGraphQL fetches nested data in one request with only the fields asked for; REST often needs several endpoints and returns fixed shapes (over- or under-fetching)
- 41GraphQL vs REST: HTTP cachingREST caches per URL with standard HTTP caching; GraphQL usually POSTs to one URL, so caching moves to the client or to persisted queries sent with GET
- 42Why can a failed GraphQL request return
200 OK?Many servers report field and validation errors in theerrorsarray with a 200 status, so clients must checkerrors, not just the status code - 43What happens when a non-null field fails to resolve?The
nullpropagates up to the nearest nullable parent field, possibly making all ofdatanull, and the error is added toerrors - 44How do you protect a GraphQL server from expensive queries?Limit query depth and complexity, cap list sizes with pagination, set timeouts, and for private APIs allow only known persisted queries
- 45What are persisted queries?Clients send a hash or ID instead of the full query text; requests get smaller, can be sent with GET for caching, and servers can allowlist them
- 46Paginate with cursors (connections)graphql
posts(first: 10, after: $cursor) { edges { cursor node { id title } } pageInfo { hasNextPage endCursor } }The Relay convention: passpageInfo.endCursorasafterto load the next page. - 47Schema-first vs code-firstSchema-first: write the SDL, then attach resolvers. Code-first: define types in code (classes or builder functions) and generate the schema from them
- 48How do GraphQL APIs usually handle versioning?They avoid
/v2endpoints: add new fields freely and deprecate old ones, since clients only get the fields they ask for - 49What does a normalized client cache do?Clients like Apollo Client store each object once, keyed by
__typenameandid, so updating one object refreshes every query that shows it - 50Does GraphQL require a particular database?No. It is only the API layer: resolvers can read from any database, REST service or other source, or several at once
Frequently asked questions
Is this deck about a specific GraphQL server or client?
No. It covers the GraphQL language and spec, which work the same in Apollo, GraphQL Yoga, Relay, urql or any other implementation. Resolver examples use plain JavaScript.
Is GraphQL better than REST?
Neither is better in general. GraphQL shines when clients need flexible, nested data from one endpoint; REST is simpler to cache and fine for straightforward resources.
What is the N+1 problem?
When a list field returns N items and a nested field on each item triggers its own database query, one request turns into N + 1 queries. Batching with DataLoader collapses them into two.
More in Web development
HTML & CSS
Fifty HTML and CSS cards: semantic HTML and accessible forms, specificity and cascade layers, flexbox and grid, stacking contexts and z-index, modern units and container queries.
50 cardsReact
Fifty React cards: state and re-rendering, useEffect and cleanup, memoization, refs, context, Suspense and the React 19 APIs like use, useOptimistic and useActionState.
50 cardsVue
Vue 3 fundamentals: ref vs reactive, computed and watch, props and emits, v-model, directives, slots, single-file components and Pinia stores.
50 cardsAngular
Modern Angular: standalone components, signals, inputs and outputs, dependency injection, RxJS basics, @if and @for, routing and OnPush change detection.
50 cardsNext.js
Next.js App Router basics: server vs client components, layouts, route handlers, server actions, caching, metadata, dynamic routes and middleware.
50 cardsNode.js
Node.js fundamentals: the event loop, ESM vs CommonJS, npm, streams, Buffer, fs/promises, process, error handling, worker threads and a basic HTTP server.
50 cards