2. Exploring our data

Now we know more about the journey a GraphQL query takes from client to server and back. Let's use this new-found knowledge for our Catstronauts app.

First, let's ask ourselves a few questions:

  • Where is our data stored, and how is it structured?
  • Is that structure different from our client app's needs and schema?
  • How can our resolver functions access that data?

The data that resolvers retrieve can come from all kinds of places: a database, a third-party API, webhooks, and so on. These are called data sources. The beauty of GraphQL is that you can mix any number of data sources to create an API that serves the needs of your client app.

Hand-drawn illustration depicting a GraphQL server retrieving data from data sources such as a database, REST API and a web hook

Where is our data stored?

We'll be using a REST API located at https://odyssey-lift-off-rest-api.herokuapp.com/.

Screenshot of the documentation for the Catstronauts REST API

Looking at the API documentation, it looks like there are 6 endpoints available:

GET /tracks
GET /track/:id
PATCH /track/:id
GET /track/:id/modules
GET /author/:id
GET /module/:id

How is our data structured?

The next question we need to figure out is how our data is structured in our REST API. This impacts how we'll need to retrieve and transform that data to match the fields in our schema.

For our current feature of displaying tracks on the homepage, let's start with the /tracks endpoint. To test this endpoint from the REST API documentation, we can click Try it out and then Execute. We get a JSON response back:

"id": "c_0",
"thumbnail": "https://res.cloudinary.com/dety84pbu/image/upload/v1598465568/nebula_cat_djkt9r.jpg",
"topic": "Cat-stronomy",
"authorId": "cat-1",
"title": "Cat-stronomy, an introduction",
"description": "Curious to learn what Cat-stronomy is all about? Explore the planetary and celestial alignments and how they have affected our space missions.",
"numberOfViews": 0,
"createdAt": "2018-09-10T07:13:53.020Z",
"length": 2377,
"modulesCount": 10,
"modules": ["l_0", "l_1", "l_2", "l_3", "l_4", "l_5", "l_6", "l_7", "l_8", "l_9"]

The response includes an array of tracks, which is a good start. Let's see in more detail what matches, referring back to the Track type in our GraphQL schema:

type Track {
id: ID!
title: String!
author: Author!
thumbnail: String
length: Int
modulesCount: Int

The array includes properties that we need in our homepage Track Card:

  • id
  • thumbnail
  • title
  • modulesCount
  • length

The array also includes a bunch of stuff we don't need for now:

  • topic
  • description
  • numberOfViews
  • createdAt
  • modules

It's okay that the array includes fields that we don't need. Our resolver functions will take care of filtering the data properties to match only what the query asks for.

Which of these are true about data sources?

Notably, the array doesn't include the author information we need, such as name and photo. However, it does include an authorId. We can provide this to the /author/:id endpoint, which takes an ID parameter and returns the details of that author.

"id": "cat-1",
"name": "Henri, le Chat Noir",
"photo": "https://images.unsplash.com/photo-1442291928580-fb5d0856a8f1?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzA0OH0"

Looking at the Author type in our schema, we get everything we need:

type Author {
id: ID!
name: String!
photo: String

We'll need to call this /author/:id endpoint for each track in the array that we received in the previous call. Then we'll need to stitch the results together so that we end up with the shape of the data that our resolver, and our query, is expecting.

What is the name of the author with id cat-9?

We know our data is provided by a REST API and how that API's data is structured. Next, we'll find out how our resolver functions can access that API.