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 the name
and profilePicture
fields, which are details that appear in Airlock's user profile page.
query GetMyProfile {me {nameprofilePicture}}
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 {nameprofilePicture...HostProfileFields...GuestProfileFields}}fragment HostProfileFields on Host {profileDescription}fragment GuestProfileFields on Guest {funds}
Note: You can test this query out in GraphOS 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 {nameprofilePicture... 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 {__typenamenameprofilePicture... on Host {profileDescription}... on Guest {funds}}}
Now let's test out our query!
Open a new browser window to http://localhost:4000, and click the button to query your server in GraphOS Studio Sandbox.
Because Airlock requires authentication, we'll need to add an
Authorization
header in the Headers tab. Set the header value to eitherBearer user-1
for a host orBearer user-2
for a guest.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 {__typenamenameprofilePicture...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 inclient/src/utils.js
- The inline fragments on
Host
andGuest
in theGET_USER
query defined inclient/src/utils.js
- The inline fragment on
Host
in theUPDATE_PROFILE
mutation defined inclient/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: IntisInColor: Boolean}type YoungAdultNovel implements Book {isbn: ID!title: String!genre: String!wordCount: IntnumberOfChapters: Int}
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.
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
- Apollo Docs: GraphQL schema basics: More on enums and input types
- Apollo Docs: Unions and interfaces
- Apollo Docs: Fragments
- GraphQL Docs: Schemas and Types: More on enums, input types, and interfaces
- GraphQL Docs: Queries and Mutations: More on fragments