Overview
It's time to jump into the data source we'll be using throughout this course.
In this lesson, we will:
- Explore the Spotify REST API
- Create a class that can manage our requests to different REST endpoints
Exploring real data
The data that our 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 applications and graph consumers.
For the rest of the course, we're going to be using a lite, pared down version of the Spotify Web API. We can access the API documentation here.
How is our data source structured?
The next question we need to answer is how our data is structured in our REST API. This impacts how we retrieve and transform that data to match the fields in our schema.
Our goal is to retrieve data for featured playlists, and there's an endpoint for exactly that: GET /browse/featured-playlists
. Click Try it out, then Execute to inspect the shape of the response we get back.
This response is lengthy! You'll notice a "playlists"
key, which holds an array of playlist "items"
, which is a good start. Let's see in greater detail what matches, referring back to the Playlist
type in our GraphQL schema:
{"message": "Featured playlists","playlists": {"items": [{"collaborative": false,"id": "6Fl8d6KF0O4V5kFdbzalfW","description": "Tooth-achingly sweet beats for your sweet eats","name": "Sweet Beats & Eats"// ... other properties},{"collaborative": false,"id": "20RU4pHDte01QywpOL6ifh","description": "Set the barbecue mood. Upbeat and laid-back tracks complement the sizzle of the grill, turning your outdoor cooking sessions into a flavorful experience. For those who savor good music and great food, it's the perfect playlist","name": "Grilling Tunes"// ... other properties}// other playlist objects]}}
Here are the fields we need from our schema.
type Playlist {id: ID!name: String!description: String}
Each object in the "items"
array includes all of these properties, along with a bunch of properties that we don't need for now—images
and followers
, to name a few!
Setting up our data source
We know where our data is, and we understand how it's structured. Awesome. Now, we need a way to request everything it has to offer!
Because it's a very common task to fetch data from REST when building a GraphQL API, Apollo Server provides a dedicated DataSource
class for just that: the RESTDataSource
.
Let's add this RESTDataSource
class to our project.
In the terminal, in the root of our project, run:
npm install @apollo/datasource-rest
Now, we should start by creating a file that can hold all of the logic specific to this Spotify service—we'll call it spotify-api.ts
, and we'll store it in a new folder called datasources
.
📂 src┣ 📂 datasources┃ ┣ 📄 spotify-api.ts┣ 📄 graphql.d.ts┣ 📄 index.ts┗ 📄 schema.graphql
First, we import the RESTDataSource
class from the @apollo/datasource-rest
package.
import { RESTDataSource } from "@apollo/datasource-rest";
We'll declare a class called SpotifyAPI
that extends RESTDataSource
. While we're here, let's export it before we forget!
export class SpotifyAPI extends RESTDataSource {// ...}
Next, let's assign our REST API's baseURL
. This property is used as the prefix to all the calls.
export class SpotifyAPI extends RESTDataSource {baseURL = "https://spotify-demo-api-fe224840a08c.herokuapp.com/v1/";}
Note: Be sure that your SpotifyAPI
class' baseURL
value ends with a /
. This will allow our helper class to make requests and append new paths to the baseURL
without any errors.
Data source methods
We need to give our class a method that can reach out to the REST API endpoint that returns playlist data. Let's call it getFeaturedPlaylists
.
getFeaturedPlaylists() {// TODO}
The RESTDataSource
class provides helper methods for HTTP requests. In our case, we want to perform a GET
request to the browse/featured-playlists
endpoint.
getFeaturedPlaylists() {this.get("browse/featured-playlists");}
The this.get
method is a generic function that lets us pass it a hand-crafted return type. From exploring our JSON response, we know that we get back an object with a playlists
key, which contains an array of items
. We'll define the return type of items
as an array of any
types for now, but we'll improve on this later.
getFeaturedPlaylists() {this.get<{ playlists: { items: any[] }}>("browse/featured-playlists");}
Let's create a new constant to hold the response, called response
, and then return the data on response.playlists.items
.
getFeaturedPlaylists() {const response = this.get<{ playlists: { items: any[] }}>("browse/featured-playlists");return response?.playlists?.items ?? [];}
Here, we're using optional chaining (?.
) and the nullish coalescing operator (??
) to validate in-line that each level of our response
object exists, and return an empty array []
if not.
Finally, because this.get
returns a promise, we need to await
the results and make our entire getFeaturedPlaylists
method async
.
async getFeaturedPlaylists() {const response = await this.get<{ playlists: { items: any[] }}>("browse/featured-playlists");return response?.playlists?.items ?? [];}
Fantastic! Our file is complete for now.
Practice
Key takeaways
- Bringing a new data source into our GraphQL API starts with assessing the shape of its responses, and determining how best to map them to our schema fields.
- We can extend the
RESTDataSource
class to introduce a REST API as a data source to our GraphQL server.
Up next
Our data source is ready to use! But how do we actually use the SpotifyClient
class in our GraphQL server?
In the next lesson, we'll explore resolver functions, and see just how we can populate data for fields in our GraphQL schema.
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.