1. Journey of a GraphQL query

👋 Welcome to Part 2 of our Lift-off series!

In Part 1, we built the homepage grid feature for the Catstronauts app, our learning platform for cats who want to explore the universe. We designed that feature with a schema-first approach and mocked static data. We displayed a single mocked entry multiple times to populate the cards of our homepage grid.

Now it's time to connect this app to real live data!

By the end of this course, your Catstronauts homepage will look like this:

Screenshot of the Catstronauts homepage in a browser showing live data

Ignition sequence...


Our app uses Node.js on the backend and React on the frontend. For this course, we'll be working only on the backend.

Clone the repository

In the directory of your choice with your preferred terminal, clone the app's starter repository:

git clone https://github.com/apollographql/odyssey-lift-off-part2


Project structure

This repo picks up where Part 1 left off. Our project is a full-stack app with the back-end app in the server/ directory and the front-end app in the client/ directory.

You'll also find a final/ folder that contains the final state of the project once you've completed the course. Feel free to use it as a guide!

Here's the file structure:

┣ 📂 client
┃ ┣ 📂 public
┃ ┣ 📂 src
┃ ┣ 📄 README.md
┃ ┣ 📄 package.json
┣ 📂 server
┃ ┣ 📂 src
┃ ┃ ┣ 📄 index.js
┃ ┃ ┣ 📄 schema.js
┃ ┣ 📄 README.md
┃ ┣ 📄 package.json
┣ 📂 final
┃ ┣ 📂 client
┃ ┣ 📂 server
┗ 📄 README.md

Now, open the repository in your favorite IDE.

Note: The examples in this course use npm, but you're welcome to use yarn if you prefer.

Let's start with the server app.

In a terminal window, navigate to the repo's server directory and run the following to install dependencies and run the app:

npm install && npm start

If all goes well, you'll see the installation complete and a message in the console indicating that the server is running.


Next, the client app.

In a new terminal window, navigate to the repo's client directory and run the following to install dependencies and start the app:

npm install && npm start

The console should show a bunch of output and a link to the running app at localhost:3000. You can navigate to localhost:3000 in the browser and see our homepage, which shows one Track card repeating a few times. This is the mock data we set up in Part 1.


To understand what our GraphQL server is missing to work with live data, and how it will know where to fetch what, let's take a step back and explore the journey of a GraphQL query.

Journey of a GraphQL query

Centered text 'The Journey of a GraphQL Query' with accompanying doodles of GraphQL components around

In client land

Our web app needs to fetch remote data to populate its homepage.
To get that data, it sends a query to our GraphQL server. The app shapes the query as a string that defines the selection set of fields it needs. Then, it sends that query 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

How does our Catstronauts client send queries to our GraphQL server?

In server land

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

If anything is off (e.g. a requested field is not defined in the schema or the query 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

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?

In this case, the query 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 field in the query, the server invokes that field's resolver 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.

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

Which of these are responsibilities of a resolver function?

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

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

When a query executes successfully, which of these is included in the object returned by the GraphQL server?

Back to client land

Our client receives the response with exactly the data it needs, passes that data to the right components to render them, and voilà, our homepage is displaying its cards from remote data.

Hand-drawn illustration depicting the browser rendering the data

And that's the journey of a GraphQL query!