10. Resolvers & data sources
5m

Overview

are responsible for returning data for a specific 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 s
  • Use the generated Spotify Client to make a REST API call to an endpoint
  • Convert HTTP response types to what our and schema expects

Resolver parameters

A can accept the following parameters:

  • self: Representing the class itself. It also represnts the parent of the particular (we'll cover this later in the course).
  • Values for arguments. are used to identify, filter, or transform data. We'll also cover this later in the course.
  • info. The information about the current , such as the name, path, and more. It also contains the context 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 function.

In order to access the spotify_client object inside context, we add a new parameter to the function called info, which has type strawberry.Info.

api/query.py
def featured_playlists(self, info: strawberry.Info) -> list[Playlist]:

When a has a parameter of type strawberry.Info, Strawberry will automatically pass the info object to the .

From the info object, we can extract spotify_client and assign it to a .

api/query.py
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.

api/query.py
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 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 , we'll call the get_featured_playlists.asyncio function, passing in the spotify_client into the client .

We'll await the call and store the results inside a called data.

api/query.py
data = await get_featured_playlists.asyncio(client=spotify_client)

Returning the correct data type

Hovering over the data , 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 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.

api/query.py
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 we need (id, name and description).

api/query.py
playlists = [
Playlist(
id=strawberry.ID(playlist.id),
name=playlist.name,
description=playlist.description,
)
for playlist in items
]

Finally we can return the playlists .

api/query.py
return playlists

Perfect! Feel free to bring all of that into one clean statement (removing the need for extra ).

api/query.py
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.

api/query.py
- 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 for featured playlists.

GraphQL operation
query FeaturedPlaylists {
featuredPlaylists {
id
name
description
}
}

Look at that! We've got our featured playlists coming back from a REST API data source

http://localhost:8000

Response data

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 .

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 and description 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 , we have our short and sweet, clean, readable 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 are needed are all done on the side.

Note: As you can see, REST and can work together! Learn more about this dynamic in this video "GraphQL and REST: true BFFs - Dan Boerner / API World".

Key takeaways

  • functions can accept parameters such as values for and info.
  • By consuming a 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 and how to access them in a function.

Previous

Share your questions and comments about this lesson

This course is currently in

beta
. Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.

You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.