9. Query arguments
10m

Overview

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

In this lesson, we will:

  • Explore in
  • Add a new to the Query type
  • Pass into a

Introducing query arguments

It's time to give our Query type another entrypoint—and with it, a new feature for our API!

While we can get a list of the featured listings from our REST API, we don't yet have a way to ask for a specific listing's details. This single Query entry point doesn't meet the needs of all our mockups; and our Listing type is still missing a few to hold its description and amenities!

A screenshot of a single listing in Airlock, focused on two additional required fields

Querying the /listings/{listing_id} endpoint

Let's return to our listings REST API endpoint. So far, we've only explored the GET /featured-listings endpoint, but we have another option available for fetching a specific listing's data: GET /listings/{listing_id}.

We can use this link to make a request for a specific listing. (Notice that we've passed in the value listing-1 in place of the , {listing_id}!)

The GET /listings/{listing_id} endpoint
https://rt-airlock-services-listing.herokuapp.com/listings/listing-1

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

{
"id": "listing-1",
"title": "Cave campsite in snowy MoundiiX",
"description": "Enjoy this amazing cave campsite in snow MoundiiX, where you'll be one with the nature and wildlife in this wintery planet. All space survival amenities are available. We have complementary dehydrated wine upon your arrival. Check in between 34:00 and 72:00. The nearest village is 3AU away, so please plan accordingly. Recommended for extreme outdoor adventurers.",
// ... more listing properties
"amenities": [
{
"id": "am-2",
"category": "Accommodation Details",
"name": "Towel"
},
{
"id": "am-10",
"category": "Space Survival",
"name": "Oxygen"
}
// ... other amenities
]
}

Everything we need is here (along with some additional properties we'll put to use soon)!

To bring this functionality into our API, we'll need to add another entry point to our schema. We'll be able to specify which unique listing 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.

Adding the listing field

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

schema.graphqls
listing: Listing

We'll give the a description, and then add parentheses after listing to specify its : id, of type ID!.

schema.graphqls
"Returns the details about this listing"
listing(id: ID!): Listing

And that's it for the definition! We now have our schema up-to-date for the feature we're implementing. Onwards to our ListingService: let's add a new method to hit this endpoint!

Updating the ListingService

Back in datasources/ListingService, we'll make a new method called listingRequest that can manage the call to the endpoint for a specific listing. It will receive a String type called listingId, and return an instance of ListingModel.

datasources/ListingService
public ListingModel listingRequest(String id) {}

This endpoint is still a GET , we'll chain on a few methods: get(), uri(), and retrieve(). The endpoint also requires a listing ID, so we'll include the listingId we pass into this method as the second parameter to uri.

public ListingModel listingRequest(String id) {
return client
.get()
.uri("/listings/{listing_id}", id)
.retrieve()
}

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

public ListingModel listingRequest(String id) {
return client
.get()
.uri("/listings/{listing_id}", id)
.retrieve()
.body(ListingModel.class);
}

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

Using arguments in datafetchers

Now we can write the datafetcher method for our new Query.listing .

Open up datafetchers/ListingDataFetcher. Here we'll add a new method for our listing , just below featuredListings. Because this is still a Query type , we'll use the @DgsQuery annotation.

datafetchers/ListingDataFetcher
// ...featuredListings method
@DgsQuery
public ListingModel listing() {}

When we our API for the listing , the id we pass is automatically conveyed to this datafetcher. (Remember, the method's name, listing, 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 ListingModel listing(String id) {}

In order to clarify that this parameter corresponds with the id input we specified in our schema's listing , 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 ListingModel listing(@InputArgument String id) {}

Now we can call the new listingRequest method on ListingService! Inside of the listing method, we'll pass in the id and return the results.

@DgsQuery
public ListingModel listing(@InputArgument String id) {
return listingService.listingRequest(id);
}

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

Task!

Testing the listing field

In the Documentation panel we'll see that our Query type contains our new listing . 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 listing to add it to our .

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

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

The Explorer Operation panel, filled in with the listing operation and some additional syntax for the variable

Let's update our to GetListing to be extra clear about what we're doing with the data we request.

query GetListing($listingId: ID!) {
listing(id: $listingId) {
}
}

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

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 listingId 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 listing ID we've been testing so far: listing-1.

Add the following to the Variables section in the Explorer:

{ "listingId": "listing-1" }

Let's test out our by adding a few more for the listing we're after: title and numOfBeds.

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

The Explorer Operation panel, with a query for a particular listing's title and number of beds, along with the listing ID variable

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

query GetListing($listingId: ID!) {
listing(id: $listingId) {
title
numOfBeds
}
}

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

This works great, but we're still missing a couple of to complete our mock-up for an individual listing. When we check the REST API response for listings, we'll see that each listing JSON object contains a "description" key; so let's go ahead and update our Listing type to include this.

Back in schema.graphqls, add the description shown below.

schema.graphqls
type Listing {
id: ID!
"The listing's title"
title: String!
"The listing's description"
description: String!
"The number of beds available"
numOfBeds: Int!
"The cost per night"
costPerNight: Float!
"Indicates whether listing is closed for bookings (on hiatus)"
closedForBookings: Boolean
}

We won't need to update our ListingModel with this new property; when we restart our server, our generated Listing type that ListingModel extends will automatically become equipped with the new description .

But what about that list of amenities we see in our mock-up for a single listing?

A screenshot of a single listing in Airlock, focused on two additional required fields

We're still missing this data about each amenity—and even in our JSON response, it looks a bit more complicated than the schema we've explored so far—each amenity has an "id", "name", and "category"!

A snippet of one listing's amenity data
{
"id": "listing-1",
// ... other listing properties
"amenities": [
{
"id": "am-2",
"category": "Accommodation Details",
"name": "Towel"
},
{
"id": "am-10",
"category": "Space Survival",
"name": "Oxygen"
}
// ... other amenities
]
}

Let's tackle bringing amenity data into our API in the next lesson.

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

  • graph

  • name

  • @

  • datafetchers

  • $

  • schema

  • hardcoded

  • null

  • arguments

  • !

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

In the next lesson, we'll explore how we build a relationship between —specifically, between our Listing type and a new type we'll call Amenity.

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.