5. Query fragments
10m

Overview

In the last lesson, we learned how to use interfaces to define common attributes that are shared across different types. We also learned how to write queries for those common attributes.

But what happens when we try to write queries for other that are unique to a particular implementing type? For example, in Airlock, the Review.author returns an object that implements the User interface, which can either be a Host or a Guest. Hosts and guests have some that aren't shared with each other, like Host.listings or Guest.funds.

We know that a that returns an interface can return any that implements that interface. So how do we know what other will be on that returned object?

To answer that, we'll need another tool: query fragments.

In this lesson, we will:

  • Learn about inline and named s
  • Learn how to interfaces and implement using both inline and named s

Querying an interface's shared fields

We can a that returns an interface in the same way we've queried other types and fields.

We start with the query keyword, then the name of our , followed by curly braces. Inside the curly braces, we can query for the outlined in the interface.

Let's look at an example from Airlock. This query uses the me , which returns the logged-in User. We'll also ask for the name and profilePicture , which are details that appear in Airlock's user profile page.

query GetMyProfile {
me {
name
profilePicture
}
}

The user profile page also shows additional information, depending on whether you're a host or a guest. For a host, we want to show their profile description. For guests, we want to show the current amount of funds they have in their wallet.

We can't add these right away to our , because these fields belong to their specific Host or Guest type and the me returns a User interface. If we tried to add the funds to our existing , we'd get an error, since the User interface doesn't have a funds .

To for the additional role-specific , we need to use a fragment.

GraphQL fragments

A fragment is a subset of from an that you can reuse and share between multiple .

Named fragments

To define a , we start with the fragment keyword, then the name of the (describing what it's for). Then, we add the keyword on, followed by the 's associated type. Inside the curly braces, we list the for that associated type.

fragment HostProfileFields on Host {
profileDescription
}
fragment GuestProfileFields on Guest {
funds
}

To use these in our original , we can include them in our selection set. We do this by preceding the name of the fragment with three periods (...), similar to JavaScript's spread syntax.

query GetMyProfile {
me {
name
profilePicture
...HostProfileFields
...GuestProfileFields
}
}
fragment HostProfileFields on Host {
profileDescription
}
fragment GuestProfileFields on Guest {
funds
}

Note: You can test this out in Apollo Studio Sandbox in the next section.

These are named fragments, and they're useful for reusing between different queries.

Inline fragments

We can also achieve a similar output with inline fragments. For this, we replace the named in our with the contents of the fragment itself, keeping our three periods (...), and then specifying the type name (on Host or on Guest). This is handy when we don't need to reuse a between .

With inline , the looks like this:

query GetMyProfile {
me {
name
profilePicture
... on Host {
profileDescription
}
... on Guest {
funds
}
}
}

Now if the logged-in user is a host, we fetch their profileDescription, and if the logged-in user is a guest, we fetch their funds. (And in all cases, we fetch the user's name, and profilePicture.)

Testing the query

To see these in action, let's test out some queries using the Airlock .

Let's build the to get the profile details using __typename and inline .

query GetMyProfile {
me {
__typename
name
profilePicture
... on Host {
profileDescription
}
... on Guest {
funds
}
}
}

Now let's test out our !

  1. Open a new browser window to http://localhost:4000, and click the button to your server in Apollo Studio Sandbox.

  2. Because Airlock requires authentication, we'll need to add an Authorization header in the Headers tab. Set the header value to either Bearer user-1 for a host or Bearer user-2 for a guest.

    Screenshot of Apollo Studio Explorer. In the Headers tab, there's a header with a key called 'Authorization' and a value of 'Bearer user-1'.
  3. Copy the above into the Explorer, and run it.

The response should look like one of following:

We can also try out a similar using named (shown below). We'll get back the same data as the query with inline fragments!

query GetMyProfile {
me {
__typename
name
profilePicture
...HostProfileFields
...GuestProfileFields
}
}
fragment HostProfileFields on Host {
profileDescription
}
fragment GuestProfileFields on Guest {
funds
}

See it in the Airlock codebase

There are multiple used throughout the Airlock client code. Check out these examples:

  • The LISTING_FRAGMENT defined in client/src/utils.js
  • The inline on Host and Guest in the GET_USER defined in client/src/utils.js
  • The inline on Host in the UPDATE_PROFILE defined in client/src/pages/profile.js

Practice

Use the schema below to complete the next two code challenges:

type Query {
availableBooks: [Book]
borrowedBooks(userId: ID!): [Book]
}
interface Book {
isbn: ID!
title: String!
genre: String!
}
type PictureBook implements Book {
isbn: ID!
title: String!
genre: String!
numberOfPictures: Int
isInColor: Boolean
}
type YoungAdultNovel implements Book {
isbn: ID!
title: String!
genre: String!
wordCount: Int
numberOfChapters: Int
}
Code Challenge!

Complete the query below by declaring two named fragments. One is called PictureBookFields on the PictureBook type, retrieving fields for numberOfPictures and isInColor. Another is called YANovelFields on the YoungAdultNovel type, retrieving fields for wordCount and numberOfChapters. Use these named fragments in the GetAvailableBooks query.

Code Challenge!

Modify the query below to use inline fragments.

Key takeaways

  • A is a subset of from an , usually used to share between multiple queries and .
  • To interfaces and their implementing types, we need to use either named or inline .
    • Named can stand alone and are great for reuse across multiple queries.
    • Inline can be written and read easily within the .

Conclusion

Well done, you've learned about four new concepts that you can use when designing your own schemas!

If you're interested in digging deeper into the topics covered in this side quest, check out the list of additional resources below.

As for what's next, you can check out the Authentication & Authorization side quest, or dive into the Voyage series to learn how to modularize your using .

Additional resources

Previous