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 fields that are unique to a particular implementing type? For example, in Airlock, the Review.author field returns an object that implements the User interface, which can either be a Host or a Guest. Hosts and guests have some fields that aren't shared with each other, like Host.listings or Guest.funds.

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

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

In this lesson, we will:

  • Learn about inline and named fragments
  • Learn how to query interfaces and implement object types using both inline and named fragments

Querying an interface's shared fields

We can query a field 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 query, followed by curly braces. Inside the curly braces, we can query for the fields outlined in the interface.

Let's look at an example query from Airlock. This query uses the me operation, which returns the logged-in User. We'll also ask for for the name and profilePicture fields, 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 fields right away to our query, because these fields belong to their specific Host or Guest type and the me field returns a User interface. If we tried to add the funds field to our existing query, we'd get an error, since the User interface doesn't have a funds field.

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

GraphQL fragments

A GraphQL fragment is a subset of fields from an object type that you can reuse and share between multiple GraphQL operations.

Named fragments

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

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

To use these fragments in our original query, we can include them in our field 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 query out in Apollo Studio Sandbox in the next section.

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

Inline fragments

We can also achieve a similar query output with inline fragments. For this, we replace the named fragment in our query 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 fragment between operations.

With inline fragments, the query 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 fragments in action, let's test out some queries using the Airlock GraphQL server.

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

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

Now let's test out our query!

  1. Open a new browser window to http://localhost:4000, and click the button to query 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 query above into the Explorer, and run it.

The response should look like one of following:

We can also try out a similar query using named fragments (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 query fragments used throughout the Airlock client code. Check out these examples:

  • The LISTING_FRAGMENT defined in client/src/utils.js
  • The inline fragments on Host and Guest in the GET_USER query defined in client/src/utils.js
  • The inline fragment on Host in the UPDATE_PROFILE mutation 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 fragment is a subset of fields from an object type, usually used to share between multiple queries and mutations.
  • To query interfaces and their implementing types, we need to use either named or inline fragments.
    • Named fragments can stand alone and are great for reuse across multiple queries.
    • Inline fragments can be written and read easily within the query.

Conclusion

Well done, you've learned about four new GraphQL 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 graph using Apollo Federation.

Additional resources

Previous