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 the
context
object in our resolvers - Use the generated Spotify
Client
to make a REST API call to an endpoint - Convert HTTP response types to what our resolver and schema expects
Resolver parameters
A resolver can accept the following parameters:
self
: Representing the class itself. It also represnts theparent
of the particular field (we'll cover this later in the course).- Values for GraphQL arguments. GraphQL arguments are used to identify, filter, or transform data. We'll also cover this later in the course.
info
. The information about the current operation, such as the field name, path, and more. It also contains thecontext
object, which can be used for things like database connections, authentication information, or as we'll see in this lesson, our Spotify client!
In this lesson we'll use the info
parameter.
Accessing spotify_client
from a resolver
Open up the api/query.py
file and find the featured_playlists
resolver function.
In order to access the spotify_client
object inside context
, we add a new parameter to the resolver function called info
, which has type strawberry.Info
.
def featured_playlists(self, info: strawberry.Info) -> list[Playlist]:
When a resolver has a parameter of type strawberry.Info
, Strawberry will automatically pass the info
object to the resolver.
From the info
object, we can extract spotify_client
and assign it to a variable.
spotify_client = info.context["spotify_client"]
Sending the request for featured playlists
Now that we have the client, we can use it to make a request to the Spotify REST API. We'll use the get_featured_playlists
module from our generated client, so let's import it at the top.
from mock_spotify_rest_api_client.api.playlists import get_featured_playlists
This module has a few functions in it, but we'll use the asyncio
function specifically. There's a sync version available as well, but we recommend using the async version to avoid blocking the server on every request to the Spotify API.
We'll need to update our resolver to be async as well.
- def featured_playlists(self, info: strawberry.Info) -> list[Playlist]:+ async def featured_playlists(self, info: strawberry.Info) -> list[Playlist]:
Now we're ready to fetch the featured playlists!
Inside the featured_playlists
resolver, we'll call the get_featured_playlists.asyncio
function, passing in the spotify_client
into the client
argument.
We'll await
the call and store the results inside a variable called data
.
data = await get_featured_playlists.asyncio(client=spotify_client)
Returning the correct data type
Hovering over the data
variable, we can see that its type is SpotifyObjectFeaturedPlaylists
. 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; we want to return a list of Playlist
types.
To get to the data we actually want, we have to drill into data.playlist.items
.
items = data.playlists.items
Hovering over items
now, we can see it's a list of SpotifyObjectPlaylistSimplified
types. It's not quite a Playlist
type, but it does contain the properties we need to convert it into a Playlist
type!
Let's generate a list, mapping through each playlist
in the items
list and creating a Playlist
instance based on the fields we need (id
, name
and description
).
playlists = [Playlist(id=strawberry.ID(playlist.id),name=playlist.name,description=playlist.description,)for playlist in items]
Finally we can return the playlists
variable.
return playlists
Perfect! Feel free to bring all of that into one clean statement (removing the need for extra variables).
return [Playlist(id=strawberry.ID(playlist.id),name=playlist.name,description=playlist.description,)for playlist in data.playlists.items]
And we're good to remove the hard-coded Playlist
objects from before.
- return [- Playlist(id="1", name="GraphQL Groovin'", description=None),- Playlist(id="2", name="Graph Explorer Jams", description=None),- Playlist(id="3", name="Interpretive GraphQL Dance", description=None),- ]
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".
Key takeaways
- Resolver functions can accept parameters such as values for GraphQL arguments and
info
. - By consuming a GraphQL API instead of a REST API, we avoid dealing with large response data.
Up next
Exciting progress! In the next lesson, we'll learn about GraphQL arguments and how to access them in a resolver function.
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.