10. Query arguments
10m

Overview

We can now data from our REST API directly, but a list of featuredPlaylists is all we can ask about. Let's give our capabilities another option!

In this lesson, we will:

  • Explore in
  • Supplement the schema's Query type with an additional endpoint
  • Pass into a

Introducing query arguments

Here's our next feature: the playlist page.

A mockup of a specific Playlist page, showing a list of tracks

We'll most likely get to this page through a few ways: clicking on a specific playlist from the featured playlists page or maybe directly through a URL link.

However we get to this page, we'll need the playlist ID, which makes it a perfect use case for a .

GraphQL arguments

An argument is a value you provide for a particular in your . The schema defines the that each of your fields accepts.

Your can then use a 's provided to help determine how to populate the data for that field. Arguments can help you retrieve specific objects, filter through a set of objects, or even transform the field's returned value.

A new entry point

Pop open the schema.graphql file, and add the following to the Query type.

schema.graphql
playlist: Playlist

We'll be able to specify which unique playlist we're for by giving this an .

Let's return to our Spotify REST API documentation to see how this works. In the list of endpoints, we'll find GET /playlists/{playlist_id}. This accepts a playlist_id parameter and returns the corresponding playlist object (if one exists).

https://spotify-demo-api-fe224840a08c.herokuapp.com/v1/docs

A screenshot of the REST API "/playlist/playlist_id" endpoint

Let's try it out!

Querying the /playlists/{playlist_id} endpoint

Pass in the following playlist ID, and click Execute.

A playlist ID
6LB6g7S5nc1uVVfj00Kh6Z

Here's a snippet of some of the properties included in the response:

{
"id": "6LB6g7S5nc1uVVfj00Kh6Z",
"name": "Zesty Culinary Harmony",
"description": "Infuse flavor into your kitchen. This playlist merges zesty tunes with culinary vibes, creating a harmonious background for your cooking escapades. Feel the synergy between music and the zest of your creations.",
"tracks": {
"items": [
/* an array of track objects */
]
// more track properties
},
"collaborative": false
// more playlist properties
}

Everything we need is here—along with some additional properties we'll put to use soon!—so we can start building out our feature.

Adding the id argument

To define an for a in our schema, we add parentheses after the field name. Inside, we write the name of the argument followed by a colon, then the type of that argument, like String or Int. If we have more than one , we can separate them with commas.

Illustration showing the syntax breakdown of using GraphQL arguments

Inside the Query type in schema.graphql update the playlist to include an id .

schema.graphql
"Retrieves a specific playlist."
playlist(id: ID!): Playlist

And that's it for the definition! We now have our schema up-to-date for the feature we're implementing. Onwards to the datasource!

Updating SpotifyAPI

Let's return to our SpotifyAPI class in spotify-api.ts and give it a new method that can reach out to this endpoint.

Just below our getFeaturedPlaylists method, we'll add a new method called getPlaylist that accepts a playlistId parameter, which is a string.

datasources/spotify-api.ts
getPlaylist(playlistId: string) {
// TODO
}

This method will make a GET request to the /playlists/{playlist_id} endpoint we just tested. We can use the this.get method, passing it the endpoint and including the playlistId . Then, we'll return the results!

spotify-api.ts
getPlaylist(playlistId: string) {
return this.get(`playlists/${playlistId}`);
}

Our method's return type still shows Promise<any>. We know that the Promise will resolve to a singular Playlist type, so we can update that here.

spotify-api.ts
getPlaylist(playlistId: string): Promise<Playlist> {
return this.get(`playlists/${playlistId}`);
}

Next up: the function.

Using arguments in resolvers

Open up the resolvers.ts file. Following the same structure as our schema, we'll add a new key inside the Query object (just below the featuredPlaylists function), named playlist.

resolvers.ts
Query: {
featuredPlaylists: (_, __, { dataSources }) => {
return dataSources.spotifyAPI.getFeaturedPlaylists();
},
playlist: () => {}
},

When we our API for the playlist , the id we pass is automatically conveyed to this .

To access the , we need to use 's second position parameter, args. args is an object that contains all that were provided for the . We can destructure this object to access the id property. We'll also need to destructure the third positional parameter for its dataSources property.

resolvers.ts
playlist: (_, { id }, { dataSources }) => {},

Next, in the body of the function, we'll use the dataSources.spotifyAPI.getPlaylist, passing it the id.

resolvers.ts
playlist: (_, { id }, { dataSources }) => {
return dataSources.spotifyAPI.getPlaylist(id);
},

We need to update our generated types to account for this new playlist in our schema, but while we're here, let's update our project so it automatically regenerates types every time our schema changes.

Jump into package.json. We're going to tweak our dev and generate scripts.

package.json
"dev": "concurrently \"ts-node-dev --respawn --watch ./**/*.graphql ./src/index.ts\" \"npm run generate --watch\"",

This update lets us run two scripts concurrently: the first is ts-node-dev, which we used previously. This command reboots our app from index.ts anytime a change is made in the project. The second script that we run is npm run generate, with a --watch flag.

Next, let's update our generate script.

package.json
"generate": "graphql-codegen --watch \"src/schema.graphql\""

This addition lets us tell the Code Generator explicitly to watch our src/schema.graphql file, and rerun the codegen process. As a result, anytime our project changes, we'll get the same hot reload we've enjoyed—along with an updated types.ts file anytime our schema changes!

Cool, let's try it out. Restart your server with the following command:

npm run dev

We should see a lot more output—first and foremost, that types have been generated—followed by the usual output that the server is running.

Testing the playlist field

Let's return to the Explorer at http://localhost:4000!

In the Documentation panel we'll see that our Query type contains our new playlist . When we click into it we can even see the name and the type of data it receives as an . Let's add a new workspace tab, then click the plus button beside the playlist to add it to our .

http://localhost:4000

A screenshot of the Explorer, with the playlist field added to the Operation panel

The Explorer automatically inserts some syntax for us to make completing the easier.

query Playlist($playlistId: ID!) {
playlist(id: $playlistId) {
}
}

You'll notice something new here: a dollar sign ($) followed by the name playlistId.

The $ symbol indicates a in . The name after the $ symbol is the name of our , which we can use throughout the . After the colon is the variable's type, which must match the type of the we'll use it for. Variables are great—they let us pass argument values dynamically from the client-side so we don't have to hardcode values into our query. We'll use them every time we create a query with arguments.

In our case, we have a called playlistId that the Explorer set up for us down in the Variables section. Right now, it's set to null, but let's replace it with the playlist ID we've been testing so far: 6LB6g7S5nc1uVVfj00Kh6Z.

Add the following to the Variables section in the Explorer:

{ "playlistId": "6LB6g7S5nc1uVVfj00Kh6Z" }

Let's test out our by adding a few more for the playlist we're after: name and description.

http://localhost:4000

A screenshot of the Explorer, building out the playlist query to include its ID variable, and the name and description fields

The Operation panel of the Explorer should now look like this:

query GetPlaylist($playlistId: ID!) {
playlist(id: $playlistId) {
name
description
}
}

When we click on the run button, we see the data we're expecting!

Practice

Where can we add entry points to our schema?
Which of these are reasons to use arguments in a query?
Variables
Variables are denoted by the 
 
symbol. They are used to provide dynamic values for 
 
to avoid including 
 
 values in a query. Each one's type must match the type specified in the 
 

Drag items from this box to the blanks above

  • hardcoded

  • $

  • graph

  • name

  • schema

  • @

  • arguments

  • null

  • resolvers

  • !

Key takeaways

  • allow us to filter, customize, and further specify the data we'd like to query.
  • We can access a 's through its function's second positional parameter, args.
  • The $ symbol indicates a in . The name after the $ symbol is the name of our , which we can use throughout the . After the colon is the variable's type, which must match the type of the we'll use it for.

Up next

We hear you, you're ready for some jams! We'll add the playlist's tracks in the next lesson.

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.