8. 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

Our Query type has just a single entry point: featuredPlaylists.

schema.graphqls
type Query {
featuredPlaylists: [Playlist!]!
}

This means that while we can get a list of the featured playlists from our REST API, we don't yet have a way to ask for a specific playlist's details. This single Query entry point doesn't meet the needs of all our mockups.

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

To resolve this, we'll need to add another entry point to our schema.

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

schema.graphqls
playlist: Playlist

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

🤔 How to use arguments

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

Your datafetchers 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 that performs a search usually provides the user's search term as an 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.

🙌 Arguments in action

Let's return to our Spotify REST API documentation. 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).

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 playlist field

Inside the Query type in schema.graphqls update the playlist to:

schema.graphqls
"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 datafetcher!

Using arguments in datafetchers

The last datafetcher method we defined in the PlaylistDataFetcher class was featuredPlaylists, which maps to our Query type's featuredPlaylists . Following that pattern, we'll add a new method for our playlist just below. Because this is still a Query type we're talking about, we'll keep the @DgsQuery annotation.

datafetchers/PlaylistDataFetcher
public class PlaylistDataFetcher {
// ...featuredPlaylist method
@DgsQuery
public MappedPlaylist playlist() {}
}

Note: This method returns a MappedPlaylist instance, rather than an instance of the generated Playlist class, so we can benefit from the additional logic that maps through JSON responses and sets properties where we need them. MappedPlaylist still matches the shape of our Playlist type, so this does not violate the rules of our schema.

When we our API for the playlist , the id we pass is automatically conveyed to this datafetcher. (Remember, the method's name playlist needs to match its corresponding Query exactly!)

To receive it, and actually do something with it, we'll specify that it receives an id of type String.

@DgsQuery
public MappedPlaylist playlist(String id) {}

In order to clarify that this parameter corresponds with the id input we specified in our schema's playlist , we need to add a specific DGS annotation called @InputArgument.

Let's import it...

import com.netflix.graphql.dgs.InputArgument;

...and update our method.

public MappedPlaylist playlist(@InputArgument String id) {}

We'll use our class' instance of the SpotifyClient class to make a call to this new endpoint, and pass it the id . First, we need to make sure the SpotifyClient class knows about this endpoint, how to pass along the necessary data, and what to do with the response.

Updating the SpotifyClient

Back in datasources/SpotifyClient, let's make a new method called playlistRequest that can manage the call to the endpoint for a specific playlist. It will receive a String type called playlistId, and return an instance of MappedPlaylist.

datasources/SpotifyClient
public MappedPlaylist playlistRequest(String playlistId) {
}

Our file hasn't used MappedPlaylist yet, so let's also import it at the top.

import com.example.soundtracks.models.MappedPlaylist;

This endpoint is still a GET , we'll chain on a few methods: get(), uri(), and retrieve(). Because our endpoint requires an actual playlist ID, we'll include the playlistId we pass into this method as the second parameter to uri.

datasources/SpotifyClient
public MappedPlaylist playlistRequest(String playlistId) {
return client
.get()
.uri("/playlists/{playlist_id}", playlistId)
.retrieve()
}

Finally, we'll map the response to the MappedPlaylist class.

datasources/SpotifyClient
public MappedPlaylist playlistRequest(String playlistId) {
return client
.get()
.uri("/playlists/{playlist_id}", playlistId)
.retrieve()
.body(MappedPlaylist.class);
}

Note: Just as we did for the featuredPlaylistsRequest, we've made the name of playlistRequest extra verbose to avoid confusion with the playlist method on PlaylistDataFetcher. These are separate methods, and we'll call this playlistRequest method from our playlist datafetcher shortly.

Updating the datafetcher

Time to jump back to our datafetcher, and call this new method on SpotifyService! Inside of the playlist method, we'll pass in the id and return the results.

datafetchers/PlaylistDataFetcher
@DgsQuery
public MappedPlaylist playlist(@InputArgument String id) {
return spotifyClient.playlistRequest(id);
}

Let's test it out! Restart your server, then return to the Explorer to write out a new .

Task!

Testing the playlist field

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 .

https://studio.apollographql.com/sandbox/explorer

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.

https://studio.apollographql.com/sandbox/explorer

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!

This works great, but our schema's MappedPlaylist type still feels a little lacking. After all, playlists are meant to contain something, right? Circling back to the mockup—we can see that we're missing data about tracks!

A specific Playlist's layout, showing the tracks it contains and several properties

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

  • schema

  • null

  • hardcoded

  • @

  • arguments

  • datafetchers

  • !

  • name

  • graph

  • $

Key takeaways

  • allow us to filter, customize, and further specify the data we'd like to query.
  • We can refer to passed directly into with the DGS @InputArgument annotation.
  • We can use the $ symbol in the Explorer to specify .

Up next

There's plenty more data to pull from in our REST API's endpoints, but we need our to keep pace. In the next lesson, we'll explore how we build a relationship between —specifically, between our Playlist type and a new type we'll call Track.

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.