2. What is GraphQL?
5m

Overview

In this lesson, we will:

  • Explore the journey of how a client requests data and how a retrieves it
  • Learn about the components that make up a
  • Learn about the basics of the syntax: , or

Journey of a GraphQL operation

Our app needs to fetch data for a particular page.

To get that data, it sends a to our . The app shapes the operation as a string that defines the selection set of it needs. Then, it sends that operation to the server in an HTTP POST or GET request.

Hand-drawn illustration depicting client-land with a browser sending a query to server-land across a network

In server-land

When our server receives the HTTP request, it first extracts the string with the . It parses and transforms it into something it can better manipulate: a tree-structured called an AST (Abstract Syntax Tree). With this AST, the server validates the against the types and in our schema.

If anything is off (e.g. a requested is not defined in the schema or the is malformed), the server throws an error and sends it right back to the app.

Hand-drawn illustration depicting server-land with the GraphQL server receiving a query and going through the necessary steps

In this case, the looks good, and the server can "execute" it. Meaning, the server can continue its process and actually fetch the data. The server walks down the AST.

For each in the , the server invokes that field's function. A resolver function's mission is to "resolve" its field by populating it with the correct data from the correct source, such as a database or a REST API. These don't necessarily need to live within the , they can be externally hosted.

In this way, is a powerful bridge to REST (and other !) that ties all of your app's data together. The GraphQL API acts as the layer on top of them, providing a single interface through which multiple data sources can be queried simultaneously.

Hand-drawn illustration depicting a resolver function retrieving data from data-land

As all of the 's are resolved, the data is assembled into a nicely ordered JSON object with the exact same shape as the .

The server assigns the object to the HTTP response body's data key, and it's time for the return trip, back to our app.

Hand-drawn illustration depicting the server returning a response back to the browser in client-land

Back to client-land

Our client receives the response with exactly the data it needs and passes that data to the right components to render them.

And that's the journey of a !

Now that we have a birds-eye view of the full journey from client, to server, and back, we'll take some time to zoom into two pieces of the story: the anatomy of a server and the anatomy of a operation.

Anatomy of a GraphQL server

The is where all the magic happens. Let's dive deeper into the components that make it up.

The schema

The schema is a collection of types and that make up the comprehensive picture of everything we can do with the data in a . No actual data lives here; just the basic skeleton of the shapes that the live data will conform to. (Think of a blueprint!)

Schema definition language (SDL)

The has its own language called schema definition language, or SDL.

We'll briefly cover the syntax for , but it's not the purpose of the course! In the next lesson, we'll see exactly how Hot Chocolate, the framework we'll be using, lets us implement GraphQL in C#, without worrying about the specifics of SDL syntax. However, it's still helpful to be familiar with SDL, since it will be the common language when discussing the schema between API and client app teams.

Let's look at an example of a schema.

GraphQL example schema
type Fruit {
name: String!
quantity: Int
averageWeight: Float
hasEdibleSeeds: Boolean
nutrients: [String]
}

The Fruit type is an example of an object type. We define an using the type keyword, then the name of the object, followed by curly braces. Inside the curly braces, we define the associated with that type.

A starts with the name of the field, then a colon (:) and then the type for that . In the example above, all the fields in the Fruit type are types.

The available scalar types in are String, Int, Float, Boolean and ID. These are similar to types in any programming language. The square brackets ([]) indicate a List type.

We can visualize the schema as a graph, a data model of nodes and edges. Each or type is a node and each relationship (the object's ) is an edge between two nodes.

Diagram of the Fruit schema

Here's how C# types map to types:

C# typeGraphQL type
stringString
intInt
doubleFloat
boolBoolean
string, int, longID
List<string>[String]

In terms of nullability, denotes non-nullable (or required) with an exclamation mark (!) after the type.

In contrast, in C#, all types are non-nullable by default. Nullable types are denoted with a ?.

can also return . For example, in the schema below, we have a Vendor type that contains a fruitCatalog , which returns a list of Fruit types. We can also add a new in the Fruit type called soldBy that returns a Vendor type.

GraphQL example schema
type Fruit {
name: String!
quantity: Int
averageWeight: Float
hasEdibleSeeds: Boolean
nutrients: [String]
soldBy: Vendor
}
type Vendor {
name: String!
fruitCatalog: [Fruit]
}

This is where we really start to see the graph shine in : when we model and traverse the relationships between our types.

Diagram of the Fruit and Vendor relationship

To make use of this , we need an entry point into the graph.

Entry points

A (the thing that the client sends to the ) can either be a query, a mutation, or a subscription. A reads data, a changes data and a listens for live, streaming data.

All three map to a corresponding type in the schema: Query, Mutation and Subscription. Let's take the as an example.

GraphQL example schema
type Query {
mostPopularFruit: Fruit
}

We can think of the within the Query type as the list of things we can ask for from our API. Similarly, the Mutation type is the list of things we can do with our API.

The resolver functions

For every type and in the , we need to define a resolver function. This is a function that can retrieve the data for a specific . These functions have access to various : databases, REST APIs, even text files or JSON!

for in our schema follow the hierarchy of the schema. This means that resolver functions for the name and quantity on a Fruit type are methods that belong to a Fruit class. This keeps our functions just as organized as our schema.

Anatomy of a GraphQL operation

The journey starts with a operation sent from the client. We described a as "a string that defines the selection set of [the client] needs". When we write this , we use the schema as our reference guide for what types and we have access to, and how they relate to each other.

We know that a can be a , a or a . Let's check out what an example of a query might look like:

GraphQL example query
query GetMostPopularFruit {
mostPopularFruit {
name
hasEdibleSeeds
soldBy {
name
}
}
}

We can already see how the schema was used as a reference for this .

First, we start with the type of , in this case query, followed by an of our choosing, GetMostPopularFruit, then curly braces. Inside, we can start to add from the Fruit type: name, hasEdibleSeeds, soldBy. And since soldBy is a that returns a Vendor type, we add from that type as well, like name.

Visually, we can also follow this as traversing the graph.

Diagram traversing the graph using the query

In this course, we'll be using a tool called Explorer that will help us build queries easily, without needing to know the specifics of the syntax we should be following.

Practice

Which of these are actions that our GraphQL server takes when it receives a request?
Which of these are situations where our GraphQL server will throw an error?
Which of these are responsibilities of a resolver function?
When a query executes successfully, which of these is included in the object returned by the GraphQL server?
GraphQL operations
There are three types of GraphQL operations. A 
 
 reads data. A mutation 
 
 data. A 
 
 listens for live, streaming data.

Drag items from this box to the blanks above

  • question

  • changes

  • subscription

  • connection

  • screenshots

  • query

Key takeaways

  • There are three types of : queries, and . A reads data, a changes data and a listens for live, streaming data.
  • The schema is a collection of types and that make up the comprehensive picture of everything we can do with the data in a . It is written in ().
  • A function retrieves the data for a specific in our schema. These functions have access to various : databases, REST APIs, even text files or JSON. These data sources don't need to live within the .

Up next

This course is all about building a , so let's get into it! Grab a warm drink and get ready, it's time to meet Hot Chocolate.

Previous

Share your questions and comments about this lesson

This course is currently in

beta
. Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.

You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.