Mental models for teaching GraphQL to beginners

This article is from a talk I recently gave at GraphQL Summit in San Francisco titled “Teaching GraphQL — Lessons from a beginner” (See talk here 👉https://youtu.be/RNLNyRm8yAQ) The talk discussed differences between beginner and experienced users of GraphQL.

When you are new to GraphQL, your understanding of the domain is very different than an expert. As a beginner, you don’t know what you don’t know and often make false associations based on surface similarities (e.g. “Is GraphQL built for graph databases? A: No). Without context, you rely on related domain knowledge to fill in the blanks. An experienced user benefits from both knowledge and context. They have built a usable mental model for GraphQL and therefore, are better able to remember and retrieve new information because their domain knowledge is organized in a schema.

Mental models are conceptual frameworks consisting of generalizations and assumptions which we use to understand the workings of things

In this article, I will introduce a mental map of GraphQL. I will begin with familiar concepts like client-server basics, RESTful APIs, CRUD operations, and HTTP methods. As I introduce GraphQL, I will show the relationship between these concepts and extend the API mental model.

What we already know about APIs

The most well-known interface between clients and servers is a RESTful API. A RESTful API must follow 5 constraints:

  1. Client-server model —a client requests data from a separated server, often over a network
  2. Uniform interface — all clients and servers interact with the API in the same way (e.g., multiple resource endpoints)
  3. Layered system — a client doesn’t have to be connected to the end server
  4. Statelessness — a client holds the state between requests and responses
  5. Cacheability — a client can cache a server’s reponse

Common RESTful APIs implement CRUD. There are 4 CRUD operations: Create, Read, Update, and Delete. These operations are defined on each resource creating an endpoint that maps to a HTTP request method.

  • HTTP POST request > Creates a resource
  • HTTP GET request > Reads data for a resource
  • HTTP PUT request > Update a resource’s data
  • HTTP DELETE request > Deletes a resource
Example data query with a common RESTful API

Introducing key ideas in GraphQL

GraphQL is a query language for APIs. A big advantage of GraphQL is it allows clients to dictate the form of data returned by the server. This solves over-fetching and gives API developers insight into which fields are used by a client.

GraphQL follows RESTful constraints but uses new concepts like a data graph, type system and resolvers.

Unlike our common interface, GraphQL organizes all the server’s data in a graph structure (versus by resources) defined by one interface (versus multiple endpoints). Objects are represented by nodes and relationships between these objects are described by the graph’s edges.

A partial Shopify data graph

For each node an object type is defined in the GraphQL schema. GraphQL defines some object types out of the box. For example, query type and scalar types (e.g. integer, string, and boolean). But, the API developer is responsible for defining the rest of the queryable objects using the GraphQL Schema Definition Language (SDL)

type Shop {
name: String!
products: [Product!]
}

Each object type is backed by a resolver. The resolver is responsible for accessing the server’s data (versus CRUD operations). Since GraphQL does not implement CRUD, there is no mapping between functions and HTTP methods. Therefore, a HTTP POST request with a query included in the body can fetch data.

Example GraphQL query

The client will send a request with the objects in the format it expects from the server. When the GraphQL server receives the client’s request, it will traverse the objects requested by the client always beginning at the query root. The resolver will execute each field on the requested object. The resulting value for each field is placed in a key-value map with the field name as the key. Some fields return another object (e.g. resource) with its own selection of fields (e.g. another resource or attribute). The resolver continues executing until scalar values (string or number) are returned. The server responds with a JSON object that mirrors the group of nested objects requested by the client.

A GraphQL service may also have a mutation type — this is a second entry point into the app data graph. Like the query type, a developer can define fields on the mutation type that a client can modify and extend the data graph.

extend type Mutation {
updateResource3(input { attribute: String1 }): mutateResource3Payload
}
type mutateResource3Payload {
resource3: Resource3!
}

The client sends a request in a similar way except the request body includes the mutation type, mutation field name it wants to execute with correct arguments, and the payload it expects back. The server then responds in a similar way except begins executing at the mutation root.

Example GraphQL mutation

Big clarifying concepts

A few key “aha” moments when learning GraphQL include:

  • Data exposed to the API is represented by a graph where objects are represented by nodes and relationships between these objects are described by edges
  • GraphQL is a RESTful API and more: a type system defines all queryable data on one endpoint
  • There is no mapping between functions implemented on the server and HTTP methods
  • Each object is backed by a resolver. The resolver is responsible for accessing the server’s data