Fran Agulto
Posted on July 20, 2023
In WPGraphQL, fragments are a feature of GraphQL in the GraphQL query languagethat allows you to define reusable selections of fields. Fragments allow you to group fields together and give them a name, which can then be used in multiple queries or mutations.
In this article, I will explain what they provide and how to use them in Faust.js. Even though we’re using Faust.js, you should be able to transfer this knowledge to any other using WPGraphQL.
Getting Started
To benefit from this post, you should be familiar with the basics of WordPress development, WPGraphQL, Faust.js, and the Apollo Client.
Steps for Local Development Set-up
WordPress Setup:
Set up a WordPress site on local, WP Engine, or any host of your choice
Set up a WordPress site on local, WP Engine, or any host of your choice
Install and activate the WPGraphQL plugin
Install and activate the Faust.js plugin
Install and activate the Advanced Custom Fields plugin
Install and activate the WPGraphQL For Advanced custom fields plugin
Faust.js Setup
Please follow the instructions to set up Faust.js in the quickstart portion of the docs here.
The Benefits of Using Fragments
Code Reusability: Fragments enable you to define a set of fields once and reuse them across different queries or mutations. This helps in reducing code duplication and promotes a modular approach to defining GraphQL operations.
Query Organization: Fragments help organize complex queries by breaking them into smaller, more manageable parts. You can efficiently compose queries and maintain a clear structure by defining fragments for specific data structures or entities.
Readability and Maintainability: Fragments make GraphQL queries more readable and understandable. Separating the fields into reusable fragments makes it easier to grasp the structure of the data being queried. Additionally, when changes are required, you can update the fragment definition, and all the queries using that fragment will be automatically updated.
How to Create and Implement Fragments for Your WPGraphQL Queries
Now that we went over the benefits of using fragments, let’s create some.
In this example, I have a custom post type called Movies
with the fields title
, id
, slug
and uri
that we want to request.
{
movies {
nodes {
title
id
slug
uri
}
}
}
The above code is a WPGraphQL query with no fragments.
This is what it would look like if we made a fragment for the above query:
{
movies {
nodes {
...MovieFragment
}
}
}
fragment MovieFragment on Movie {
title
id
slug
uri
}
Let’s break down how we created a fragment for our query.
Once we know what fields we want to pull out from our Movies
custom post type, we start making our fragment with the keyword fragment
followed by the name we choose, in this case, we call it MovieFragment
.
The on Movie
clause specifies this fragment is applicable to the Movie object type. When WPGraphQL returns a node
of type Movie
, it uses this fragment to determine what fields to return on the result.
Inside the fragment, there are several fields (title, id, slug, uri
) that correspond to the fields of the Movie type.
Lastly, we take our fragment named MovieFragment
and insert it into the main query block right under the nodes
that the original query had with the fields instead of the actual fields and just precede it by using ...
much like the JavaScript spread syntax followed by the name.
You can go to your WP Admin, navigate over to GraphiQL IDE, and create the same custom post type with the same fields. Copy my queries and try this yourself or go ahead and make your own.
Now that this is done, we get back the same results, but our code is much cleaner and organized in a reusable way.
Using Fragments in Faust.js
Now that we know how to make and implement fragments, let’s explore how Faust.js uses them out of the box with its default examples and create some custom ones.
Default Faust.js Fragments
The first fragments we will explore are one of the defaults that come with Faust.js. Let’s look at the front page template that renders when you visit the main path. This file is located at wp-templates/front-page.js
.
import { useQuery, gql } from "@apollo/client";
import * as MENUS from "../constants/menus";
import { BlogInfoFragment } from "../fragments/GeneralSettings";
import {
Header,
Footer,
Main,
Container,
NavigationMenu,
Hero,
SEO,
} from "../components";
export default function Component() {
const { data } = useQuery(Component.query, {
variables: Component.variables(),
});
const { title: siteTitle, description: siteDescription } =
data?.generalSettings;
const primaryMenu = data?.headerMenuItems?.nodes ?? [];
const footerMenu = data?.footerMenuItems?.nodes ?? [];
return (
<>
<SEO title={siteTitle} description={siteDescription} />
<Header
title={siteTitle}
description={siteDescription}
menuItems={primaryMenu}
/>
<Main>
<Container>
<Hero title={"Fran's Front Page"} />
<div className="text-center">
<p>This page is utilizing the "front-page" WordPress template.</p>
<code>./wp-templates/front-page.js</code>
</div>
</Container>
</Main>
<Footer title={siteTitle} menuItems={footerMenu} />
</>
);
}
Component.query = gql`
${BlogInfoFragment}
${NavigationMenu.fragments.entry}
query GetPageData(
$headerLocation: MenuLocationEnum
$footerLocation: MenuLocationEnum
) {
generalSettings {
...BlogInfoFragment
}
headerMenuItems: menuItems(where: { location: $headerLocation }) {
nodes {
...NavigationMenuItemFragment
}
}
footerMenuItems: menuItems(where: { location: $footerLocation }) {
nodes {
...NavigationMenuItemFragment
}
}
}
`;
Component.variables = () => {
return {
headerLocation: MENUS.PRIMARY_LOCATION,
footerLocation: MENUS.FOOTER_LOCATION,
};
};
At the very top of this file, we import the fragment we will use named BlogInfoFragment
from the fragments directory. The next thing to focus on is the bottom of the file:
Component.query = gql`
${BlogInfoFragment}
${NavigationMenu.fragments.entry}
query GetPageData(
$headerLocation: MenuLocationEnum
$footerLocation: MenuLocationEnum
) {
generalSettings {
...BlogInfoFragment
}
headerMenuItems: menuItems(where: { location: $headerLocation }) {
nodes {
...NavigationMenuItemFragment
}
}
footerMenuItems: menuItems(where: { location: $footerLocation }) {
nodes {
...NavigationMenuItemFragment
}
}
}
`;
On the template Component,
we define a query property that we assign at the top of the query block is the Component.query
syntax, a GraphQL query string used to fetch the template’s data in the Faust.js template hierarchy. This is a Faust.js convention that makes it easier to load data.
Next, we pass in our fragment, which contains the fields from the WP GenralSetttings
object type of title and description. Notice the syntax being ${BlogInfoFragment}
. This is a technique called string interpolation or template interpolation. It allows you to dynamically insert the content of the fragment variable into the query string at that specific location. This is equivalent to defining the fragment inline, which allows us to use the fragment in the actual query operation.
In this case, the ${BlogInfoFragment}
is used to include the fragment definition within the query string. The Apollo Client uses the gql
function to parse the query string and understand that the ...BlogInfoFragment
syntax is referencing the fragment named BlogInfoFragment
.
Fragments Directory
This fragment is located at fragments/GeneralSettings.js
and is imported at the top of this file.
The GeneralSettings.js
file looks like this:
import { gql } from '@apollo/client';
export const BlogInfoFragment = gql`
fragment BlogInfoFragment on GeneralSettings {
title
description
}
`;
In this code block, we export the variable called BlogInfoFragment and use the gql
client function from Apollo to define it. Then we create the fragment in the template literal and add the fields we want back in the fragment. In GraphQL, the template literal starts with the backtick character (
)
and ends with another backtick. They are used to define multi-string values for queries and mutations. This is used to define the GraphQL fragment named BlogInfoFragment
.
This is the directory in Faust.js which contains your fragment files that can be shared and reused when you import them into your components and queries.
The fragment directory is useful when you need a centralized location for all your fragments. This can be beneficial when multiple components share the same fragments or when you want to keep your component files less cluttered.
Colocated Fragments
Colocated fragments are what Faust.js uses in the majority of its components. Colocation refers to the practice of defining fragments in the same file or module as the component that uses them, keeping the fragment definition alongside the component’s code.
This is what we consider best practice within Faust.js for these reasons:
Code Organization: By colocating fragments in the same component that uses them, the code becomes more organized and maintainable.
Local Scope: Fragments defined within a component have local scope and are not exposed globally. This prevents naming collisions and reduces the likelihood of conflicts with other fragments defined elsewhere in the application.
Explicit Relationship: Colocated fragments establish an explicit relationship between the fragment and the component that uses it. When reading or modifying the component, developers understand the specific fragment(s) used, facilitating more manageable maintenance and updates.
Things to Consider/Best Practices
You could run into conflicts if you had two fragments with the same name, but different fields, it would cause an issue. For example:
# Component A
fragment PostFields on Post {
title
}
# Component B
fragment PostFields on Post {
id
}
query {
posts {
nodes {
...PostFields
}
}
}
In this case, the query is using the PostFields
fragment, but WPGraphQL does not know which version of PostFields
to use since there are two with the same name. This results in an invalid query that will not execute.
This example is valid:
fragment PostTitle on Post {
title
}
fragment PostId on Post {
id
}
{
posts {
nodes {
...PostTitle
...PostId
}
}
}
We give a unique name to our fragments ensuring each component’s fragment definition remains distinct and can be used without conflict in WPGraphQL operations.
We can even ask for the same fields in both, like so:
fragment SomeComponentOnTheTopOfThePage on Post {
id
title
author {
node {
id
name
}
}
}
fragment SomeComponentOnTheSidebar on Post {
id
title
date
featuredImage {
node {
id
sourceUrl
}
}
}
{
posts {
nodes {
...SomeComponentOnTheTopOfThePage
...SomeComponentOnTheSidebar
}
}
}
Both components need id
and title
so they both declare it. This might seem redundant but it is not. If one component no longer needs the title, it can take it out of its fragment, but the title will still be queried because the other fragment still asks for it.
Each component should query for what it specifically needs. However, components should not ask for fields they don’t actually need to satisfy another component’s needs.
Let’s shift our focus back to our wp-templates/front-page.js
file:
Component.query = gql`
${BlogInfoFragment}
${NavigationMenu.fragments.entry}
query GetPageData(
$headerLocation: MenuLocationEnum
$footerLocation: MenuLocationEnum
) {
generalSettings {
...BlogInfoFragment
}
headerMenuItems: menuItems(where: { location: $headerLocation }) {
nodes {
...NavigationMenuItemFragment
}
}
footerMenuItems: menuItems(where: { location: $footerLocation }) {
nodes {
...NavigationMenuItemFragment
}
}
}
`;
Let’s look at the bottom of the file. The colocated fragment is${NavigationMenu.fragments.entry}
. The ${NavigationMenu.fragments.entry}
syntax includes the entry fragment from the NavigationMenu
fragment collection. It allows the fields defined in the entry fragment to be used in the query.
This fragment is part of the NavigationMenu
object and is defined in the components/NavigationMenu.js
file:
import classNames from 'classnames/bind';
import { gql } from '@apollo/client';
import Link from 'next/link';
import flatListToHierarchical from '../../utilities/flatListToHierarchical';
import styles from './NavigationMenu.module.scss';
import stylesFromWP from './NavigationMenuClassesFromWP.module.scss';
let cx = classNames.bind(styles);
let cxFromWp = classNames.bind(stylesFromWP);
export default function NavigationMenu({ menuItems, className }) {
if (!menuItems) {
return null;
}
// Based on https://www.wpgraphql.com/docs/menus/#hierarchical-data
const hierarchicalMenuItems = flatListToHierarchical(menuItems);
function renderMenu(items) {
return (
<ul className={cx('menu')}>
{items.map((item) => {
const { id, path, label, children, cssClasses } = item;
// @TODO - Remove guard clause after ghost menu items are no longer appended to array.
if (!item.hasOwnProperty('__typename')) {
return null;
}
return (
<li key={id} className={cxFromWp(cssClasses)}>
<Link href={path ?? ''}>{label ?? ''}</Link>
{children.length ? renderMenu(children, true) : null}
</li>
);
})}
</ul>
);
}
return (
<nav
className={cx(['component', className])}
role="navigation"
aria-label={`${menuItems[0]?.menu?.node?.name} menu`}>
{renderMenu(hierarchicalMenuItems)}
</nav>
);
}
NavigationMenu.fragments = {
entry: gql`
fragment NavigationMenuItemFragment on MenuItem {
id
path
label
parentId
cssClasses
menu {
node {
name
}
}
}
`,
};
At the very bottom of the file are the fields we ask for in the fragment as follows:
fragment NavigationMenuItemFragment on MenuItem {
id
path
label
parentId
cssClasses
menu {
node {
name
}
}
}
The actual fragment is defined in the child component (NavigationMenu.js)
that uses it explicitly within its component. It is then reused into a parent component (front-page.js)
that consumes it and renders it along with other fragments in the query.
Custom Fragment Examples
Now that we have an example of default fragments in Faust.js, let’s explore some custom ones in this repo.
Using the custom post types we made for this article, let’s create a query block and fragments for them.
The file we are looking at is located at components/MovieCard.js
.
import { gql, useQuery } from "@apollo/client";
import Link from "next/link";
const GET_MOVIES = gql`
query GET_MOVIES {
movies {
nodes {
...MovieFragment
}
}
actors {
nodes {
...ActorFragment
}
}
}
fragment MovieFragment on Movie {
title
id
slug
uri
}
fragment ActorFragment on Actor {
title
id
slug
uri
}
`;
function MovieList() {
const { loading, error, data } = useQuery(GET_MOVIES);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h2>Movies</h2>
<ul>
{data.movies.nodes.map((movie) => (
<li key={movie.id}>
<Link href={`${movie.uri}`}>
<a>{movie.title}</a>
</Link>
</li>
))}
</ul>
<h2>Actors</h2>
<ul>
{data.actors.nodes.map((actor) => (
<li key={actor.id}>
<Link href={`${actor.uri}`}>
<a>{actor.title}</a>
</Link>
</li>
))}
</ul>
</div>
);
}
export default MovieList;
Let’s focus on the top of the file where our WPGraphQL Query and Fragment is located:
const GET_MOVIES = gql`
query GET_MOVIES {
movies {
nodes {
...MovieFragment
}
}
actors {
nodes {
...ActorFragment
}
}
}
fragment MovieFragment on Movie {
title
id
slug
uri
}
fragment ActorFragment on Actor {
title
id
slug
uri
}
`;
fragment ActorFragment on Actor {
title
id
slug
uri
}
`;
The MovieFragment
and ActorFragment
are defined inline within the GraphQL query using the fragment
keyword. They are then referenced using the spread syntax (...)
within the GET_MOVIES
query to include the fragment fields in the response which are at the bottom of the query.
Nested Fragments
We not only included the Movie data in a fragment, but we also nested the Actor data fragment in the query block. This is useful for larger applications where you could have multiple nested fragments that go on and on.
Along with the main benefits of regular fragments, nested fragments also allow developers to have composition. This enables you to build up more complex fragments by including smaller fragments within them. You can nest fragments within other fragments, creating a hierarchy of reusable building blocks. This modular approach makes composing and customizing queries based on specific needs easier.
If you have a fragment that needs to be shared across multiple components, you can have these live in the fragments folder, as stated earlier in the article. The query would look like this instead:
const GET_MOVIES = gql`
${MovieFragment}
${ActorFragment}
query GET_MOVIES {
movies {
nodes {
...MovieFragment
}
}
actors {
nodes {
...ActorFragment
}
}
}
Then we would go into the fragments folder and create our fragments there. Once that is done, we would import them into the component using them.
Conclusion
I hope this article provided a better understanding of using fragments with WPGraphQL and Faust.js.
As always, super stoked to hear your feedback and any questions you might have on headless WordPress. Hit us up on our Discord channel!
Posted on July 20, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.