Overview
Resolvers are responsible for returning data for a specific field in our schema. So far, we've returned hard-coded playlist data but it's time to replace that with a call to our REST API.
In this lesson, we will:
- Learn how to access data sources from a resolver function
- Use an
HttpClient
to make a REST API call to an endpoint - Convert HTTP response types to what our resolver and schema expects
Resolvers
So far, we've written resolver functions that don't require any parameters. For example, the Query.Hello
resolver immediately returned a string
value "Hello World".
We also had our property resolvers for a playlist's Id
, Name
and Description
, where each field's get
accessor acted as the resolver function.
But resolvers can do much more! A resolver can accept the following parameters:
- Values for GraphQL arguments. GraphQL arguments are used to identify, filter, or transform data. We'll cover this in the next lesson.
- The parent value. We'll also cover this in a later lesson.
HttpContext
. The context involved in the HTTP request sent to the server.- Data sources (or services). The services we registered with the server using dependency injection.
In this lesson, we'll focus on the data sources parameter.
Accessing data sources
In the previous lesson, we registered the SpotifyService
with our GraphQL server so that we could access it in the resolver functions.
In Query.cs
, let's make use of the SpotifyService
by adding it as a parameter inside the FeaturedPlaylists
function.
public List<Playlist> FeaturedPlaylists(SpotifyService spotifyService)
Make sure we're importing the SpotifyWeb
package at the top as well, where SpotifyService
lives (otherwise, you'll see an error!).
using SpotifyWeb;
That's it! We can use the spotifyService
instance anywhere in our resolver function body.
In the previous lesson, we did one extra step and used RegisterService<SpotifyService>
in our GraphQL server, which was optional. If we didn't do that, our resolver function would look like this instead:
public List<Playlist> FeaturedPlaylists([Service] SpotifyService spotifyService)
We would have needed to include the [Service]
attribute with the SpotifyService
to access it! Isn't it much cleaner to omit it? We think so!
GetFeaturedPlaylists
Tip: Make use of your code editor's IntelliSense features to get insight into what methods are available from the spotifyService
!
Now that we can access the spotifyService
, we can make our first HTTP call and store the response.
var response = spotifyService.GetFeaturedPlaylistsAsync();
The GetFeaturedPlaylistsAsync
method maps to the GET /browse/featured-playlists
endpoint we were exploring earlier.
This method is asynchronous, so we'll await
the results and mark the function as async
. With an asynchronous function, the return type needs to be a Task<T>
type as well.
public async Task<List<Playlist>> FeaturedPlaylists(SpotifyService spotifyService){var response = await spotifyService.GetFeaturedPlaylistsAsync();// return new List<Playlist>{...};}
Hovering over the response
variable, we can see that its type is SpotifyWeb.FeaturedPlaylists
. That's the response type we saw earlier (in the REST API docs) with the top-level properties of message
and playlists
. It's not what this resolver function should return. Instead, we have to access response.Playlists.Items
.
var items = response.Playlists.Items;
Now the items
variable is a collection of PlaylistSimplified
types (this type is defined in SpotifyWeb
). It's not quite a Playlist
type, but it does contain the properties we need to convert it into a Playlist
type!
To convert a PlaylistSimplified
type to a Playlist
type, we'll creating an additional new constructor inside the Playlist
class.
Open up Playlist.cs
and create a new constructor that takes in a PlaylistSimplified
object and sets each Playlist
property using the object's properties.
public Playlist(PlaylistSimplified obj){Id = obj.Id;Name = obj.Name;Description = obj.Description;}
Don't forget to import the SpotifyWeb
namespace at the top, since PlaylistSimplified
is coming from that package (better yet, let your code editor do that auto-import for you!)
using SpotifyWeb;
Now we can use this constructor back in the Query.FeaturedPlaylists
resolver. We'll use the Select
function to map over each PlaylistSimplified
object in the items
collection and return a new Playlist
object.
var playlists = items.Select(item => new Playlist(item));
Finally, we need to convert the collection into a list using ToList()
so it matches that List<Playlist>
type the resolver wants to return!
return playlists.ToList();
Perfect! Feel free to bring all that into one clean line.
return response.Playlists.Items.Select(item => new Playlist(item)).ToList();
And we're good to remove the hard-coded Playlist
objects from before.
- return new List<Playlist>- {- new Playlist("1", "GraphQL Groovin'"),- new Playlist("2", "Graph Explorer Jams"),- new Playlist("3", "Interpretive GraphQL Dance")- };
Explorer time!
Excited to see what all that code did?! Make sure all files have been saved and the server is running with the latest changes.
Let's jump over to Sandbox Explorer and run that same query for featured playlists.
query FeaturedPlaylists {featuredPlaylists {idnamedescription}}
Look at that! We've got our featured playlists coming back from a REST API data source ✨
Comparing with the REST approach
Let's put on our product app developer hat on for a minute and compare what this feature would have looked like if we had used REST instead of GraphQL.
If we had used REST, the app logic would have included:
- Making the HTTP
GET
call to the/browse/featured-playlists
endpoint - Digging into the response JSON's
playlists.items
property - Retrieving just the
id
,name
anddescription
properties, discarding all the rest of the response.
There's so much more to the response that wasn't used! If the client app had slow network speeds or not much data, that big response comes with a cost.
With GraphQL, we have our short and sweet, clean, readable operation coming from the client, coming back in exactly the shape they specified, no more, no less!
All the logic of extracting the data and filtering for which fields are needed are all done on the GraphQL server side.
Note: As you can see, REST and GraphQL can work together! Learn more about this dynamic in this video "GraphQL and REST: true BFFs - Dan Boerner / API World".
Practice
Key takeaways
- Resolver functions can accept parameters such as values for GraphQL arguments, the
parent
value, HttpContext, and data sources. - By consuming a GraphQL API instead of a REST API, we avoid dealing with large response data.
Up next
Exciting progress! But you're probably thinking — what are playlists without songs? In the next few lessons, we'll tackle the next feature: a playlist's tracks.
Share your questions and comments about this lesson
This course is currently in
You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.