askesis

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.

Web developmentBeginner50 cardsFree

Practice now

Study without an account

0 of 50 seen0 graduated
What is GraphQL?

Scheduled with the default Askesis timings. Progress is saved in this browser.

All 50 cards

  1. 1
    What 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
  2. 2
    How is a GraphQL API usually served over HTTP?
    From a single endpoint, often /graphql, usually as a POST with a JSON body holding query, variables and an optional operationName
  3. 3
    What 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)
  4. 4
    Define a User type with an ID, a required name and a list of posts
    graphql
    type User {
      id: ID!
      name: String!
      email: String
      posts: [Post!]!
    }
  5. 5
    What does ! mean after a type?
    Non-null: the field never returns null, or, on an argument, a value must be provided
  6. 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
  7. 7
    The five built-in scalar types
    Int (32-bit signed), Float (double precision), String, Boolean and ID
  8. 8
    What is the ID scalar?
    A unique identifier that is serialized as a string; it accepts string or integer input and isn’t meant to be human-readable
  9. 9
    What does scalar DateTime do?
    Declares a custom scalar type; the server implements how it is serialized, parsed and validated
  10. 10
    The three root operation types
    Query for reads, Mutation for writes and Subscription for real-time events
  11. 11
    What does this query return?
    graphql
    query {
      user(id: "1") {
        name
      }
    }
    json
    { "data": { "user": { "name": "Ada" } } }
    The response mirrors the shape of the query.
  12. 12
    What keys can a GraphQL response contain?
    data with the requested fields and, if anything failed, an errors array; both appear together when a result is partial
    An optional extensions key carries extra metadata such as tracing.
  13. 13
    Why is this query invalid?
    graphql
    {
      user(id: "1")
    }
    user returns an object type, and every selection must end in leaf fields (scalars or enums). Add a sub-selection like { name }
  14. 14
    Pass an argument with a variable
    graphql
    query GetUser($id: ID!) {
      user(id: $id) {
        name
      }
    }
    Variables travel separately as JSON, e.g. { "id": "42" }. A default looks like $limit: Int = 10.
  15. 15
    Why 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
  16. 16
    What is GetUser in query GetUser { … }?
    The operation name: it appears in logs and tracing, and selects which operation to run when a document holds several
    A single query may use the anonymous shorthand { me { name } }.
  17. 17
    Request the same field twice with different arguments
    graphql
    {
      thumb: avatar(size: 64)
      full: avatar(size: 512)
    }
    Aliases rename fields in the result so they don’t collide.
  18. 18
    What is a fragment?
    A reusable selection of fields on a type, spread into queries with ...
    graphql
    fragment UserCard on User {
      id
      name
      avatarUrl
    }
    
    query {
      me { ...UserCard }
    }
  19. 19
    Query a field that returns a union type
    graphql
    {
      search(text: "ada") {
        __typename
        ... on User { name }
        ... on Post { title }
      }
    }
    Inline fragments (... on Type) select fields per concrete type.
  20. 20
    What does __typename return?
    The name of the object’s concrete type, like "User"; clients use it with unions, interfaces and cache normalization
  21. 21
    Fetch a field only when a variable is true
    graphql
    query Profile($withAvatar: Boolean!) {
      me {
        name
        avatarUrl @include(if: $withAvatar)
      }
    }
    @skip(if: …) is the inverse.
  22. 22
    How 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
  23. 23
    Define an enum type
    graphql
    enum Role {
      ADMIN
      EDITOR
      VIEWER
    }
    Enum values are serialized as their names, like "ADMIN".
  24. 24
    Interface vs union
    An 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
  25. 25
    Why do mutation arguments use input types?
    Arguments must be input types (scalars, enums or input objects); regular object types can’t be used as arguments
    graphql
    input CreatePostInput {
      title: String!
      body: String
    }
  26. 26
    Write a mutation that creates a post
    graphql
    mutation CreatePost($input: CreatePostInput!) {
      createPost(input: $input) {
        id
        title
      }
    }
    Returning the changed object lets clients update their caches without refetching.
  27. 27
    How 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
  28. 28
    What 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 events
    graphql
    subscription {
      messageAdded(roomId: "1") {
        text
      }
    }
    A subscription must select exactly one root field.
  29. 29
    What is a resolver?
    A function that produces the value of one field; it receives (parent, args, context, info)
  30. 30
    What 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)
  31. 31
    What belongs in the resolver context?
    Per-request shared things: the authenticated user, database clients and DataLoaders. It is created fresh for every request
  32. 32
    What is user in the User.posts resolver?
    js
    const resolvers = {
      Query: {
        user: (_parent, { id }, { db }) => db.users.find(id),
      },
      User: {
        posts: (user, _args, { db }) => db.posts.byAuthor(user.id),
      },
    }
    The User object returned by the parent resolver, Query.user; resolvers run top-down, each receiving its parent’s result
  33. 33
    What is the N+1 problem in GraphQL?
    Fetching a list of N posts with one query, then resolving each post’s author with its own query: 1 + N database round trips
  34. 34
    How does DataLoader fix N+1?
    It collects every load(key) made in the same tick, calls your batch function once with all the keys (one WHERE id IN (…) query) and caches results for the request
    js
    const 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)
  35. 35
    What must a DataLoader batch function return?
    A promise of an array with the same length and order as the keys, holding null or an Error for keys that weren’t found
  36. 36
    Why create DataLoaders per request?
    Their cache would otherwise outlive the request, serving stale data and leaking one user’s results to another
  37. 37
    What is introspection?
    Querying the schema itself through fields like __schema and __type; it powers GraphiQL, editor autocomplete and code generators
  38. 38
    What does this query return?
    graphql
    {
      __type(name: "User") {
        fields { name }
      }
    }
    The names of all fields on the User type, read from the schema through introspection
  39. 39
    Should 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
  40. 40
    GraphQL vs REST: fetching data
    GraphQL 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)
  41. 41
    GraphQL vs REST: HTTP caching
    REST 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
  42. 42
    Why can a failed GraphQL request return 200 OK?
    Many servers report field and validation errors in the errors array with a 200 status, so clients must check errors, not just the status code
  43. 43
    What happens when a non-null field fails to resolve?
    The null propagates up to the nearest nullable parent field, possibly making all of data null, and the error is added to errors
  44. 44
    How 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
  45. 45
    What 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
  46. 46
    Paginate with cursors (connections)
    graphql
    posts(first: 10, after: $cursor) {
      edges {
        cursor
        node { id title }
      }
      pageInfo { hasNextPage endCursor }
    }
    The Relay convention: pass pageInfo.endCursor as after to load the next page.
  47. 47
    Schema-first vs code-first
    Schema-first: write the SDL, then attach resolvers. Code-first: define types in code (classes or builder functions) and generate the schema from them
  48. 48
    How do GraphQL APIs usually handle versioning?
    They avoid /v2 endpoints: add new fields freely and deprecate old ones, since clients only get the fields they ask for
  49. 49
    What does a normalized client cache do?
    Clients like Apollo Client store each object once, keyed by __typename and id, so updating one object refreshes every query that shows it
  50. 50
    Does 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.