6. Implementing query resolvers
4m

In src/resolvers.js, in our tracksForHome resolver, let's add the parameters.

The order of the parameters matters here. If we only added one parameter, then the function would consider it as the first optional parameter: parent. We need the context, which is the third parameter, to access our data sources.

We don't need the first two parameters, so as a convention, we'll name them with underscores: one underscore for the first (parent) and two underscores for the second (args).

For the context, we'll destructure it to access its child object dataSources. And we can omit the fourth parameter, info, as we won't use it.

const resolvers = {
Query: {
// get all tracks, will be used to populate the homepage grid of our web client
tracksForHome: (_, __, {dataSources}) => {}
}
};
1
2
3
4
5
6

Code Challenge!

Edit the parameters for the resolver function of the spaceCats field to follow the conventions explained above. You'll need to destructure dataSources from the context parameter. The function should not return anything for now.

From our dataSources object, we'll gain access to our trackAPI (lowercase here as it's the instance of our TrackAPI class extending RESTDataSource) and its getTracksforHome method that we built earlier.

Our tracksForHome resolver will return the results from that TrackAPI method.

const resolvers = {
Query: {
// get all tracks, will be used to populate the homepage grid of our web client
tracksForHome: (_, __, {dataSources}) => {
return dataSources.trackAPI.getTracksForHome();
}
}
};
module.exports = resolvers;
1
2
3
4
5
6
7
8
9
10

Code Challenge!

Create a resolver function for the Query.spaceCats field. Follow the conventions explained above for the 4 optional parameters. Use the dataSources object to access the spaceCatsAPI.getSpaceCats() method and return the results.

Now, that takes care of getting the data for our tracks, but we also need the author associated with each track. We need to call the getAuthor method from our TrackAPI.

So where should this call be located? Well, our tracksForHome resolver could map over the returned tracks data, take each track's authorId property, and call getAuthor with it. But then our tracksForHome resolver would always fetch author data, even for queries that don't even request it!

Instead, let's add another resolver specifically for a track's author. To do this, we'll add another key to our resolvers object called Track, indicating that it's for the Track type in our schema. Inside that Track key will be another object with an author field, where we'll define our resolver.

const resolvers = {
Query: {
// ...
},
Track: {
author: (parent, args, context, info) => {}
}
};
1
2
3
4
5
6
7
8

This time we'll need the parent argument, so let's keep it in the resolver function. We can replace args with an underscore. We'll destructure context to access the dataSources key for our TrackAPI. And then omit info because we don't need it.

const resolvers = {
Query: {
// ...
},
Track: {
author: (parent, _, {dataSources}) => {}
}
};
1
2
3
4
5
6
7
8

The TrackAPI's getAuthor method needs an authorId. We'll get this value from the parent argument passed to the resolver. The parent argument contains data returned by our tracksForHome resolver, and because tracksForHome returns a list, Apollo Server iterates through that list and calls the author resolver once for each track. It passes the current track as the value of parent, enabling us to extract the authorId.

If we were to console.log(parent), inside our author resolver, the printed value would look exactly like a single raw track from the tracks list returned by our RESTDataSource.

Which information will be present in the parent argument of our Track.author resolver?

Let's destructure authorId from the parent argument. Then, we'll call the getAuthor method from our dataSources.trackAPI method, passing in the authorId. Finally, we'll return the result.

// src/resolvers.js
const resolvers = {
Query: {
// get all tracks, will be used to populate the homepage grid of our web client
tracksForHome: (_, __, {dataSources}) => {
return dataSources.trackAPI.getTracksForHome();
}
},
Track: {
author: ({authorId}, _, {dataSources}) => {
return dataSources.trackAPI.getAuthor(authorId);
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Code Challenge!

Create a resolver function for the SpaceCat.missions field. Follow the conventions explained above for the 4 optional parameters. Use the dataSources object to access the spaceCatsAPI.getMissions() method. It takes a catId argument from the parent and returns the results.

As a best practice, when working on your resolvers and data sources, try to keep resolver functions as thin as possible. By doing so, you make your API more resilient to future changes. You can safely refactor your data fetching code, or change the source entirely from a REST API to a database, without breaking your API. This also keeps your resolvers readable and easier to understand, which comes in handy as you define more and more of them!

Now, you might be wondering where the dataSources object that's passed to our resolver comes from. When did we add it to the context parameter? Right now, it isn't added. If we ran our server, it would have no clue about our TrackAPI. It also doesn't know about our resolvers yet! Let's make sure all three pieces are connected with each other.

Previous
Next