Working with the Apollo Client in Faust.js
Tags: nextjs, apollo, wordpress, webdev
Published: 2023-03-24 14:35:51

In this article, I will discuss how to work with the  Apollo Client, the new GraphQL client library that is used in the Faust.js framework.

## The New Faust.js

[Faust.js is the front-end JavaScript framework](https://faustjs.org/) built on top of Next.js, created to make developing headless WordPress sites easier. The idea of this new version is to make a parity between the WordPress world that a developer might be used to but replicate it as much as possible in the JavaScript front-end world. 

## The Apollo Client

Faust uses Apollo for its GraphQL client.  The [Apollo client](https://www.apollographql.com/docs/) is a data-fetching library for JavaScript that enables you to manage both local and remote data with GraphQL.  It has features you can use for fetching, modifying app data, and caching.  We are going to focus on how to use it in Faust.

### Usage in Faust.js

Within Faust, you can use the client in your components and pages.  Take this example here from the [Faust docs](https://faustjs.org/docs/apollo):

```
import { gql, useQuery } from '@apollo/client';

export default function Page(props) {
  const { data } = useQuery(Page.query);
  const { title, description } = data.generalSettings;
  return(
    <h1>{title}</h1>
    <p>{description}</p>
  )
}

Page.query = gql`
  query {
    generalSettings {
      title
      description
    }
  }
`
```
Let’s go over what is happening in this file.  At the top of the file, `graphql` and the `useQuery` hook from Apollo is being imported into the file.  Then we have a default function called `Page` that will render the `title` and `description` of the general settings from your WPGraphQL API.  After passing the props into the `Page` function, we create a variable that contains the `data` object that is the destructured response from the `useQuery` hook provided by Apollo. This then takes `Page.query` as a parameter.

At the bottom of the file is our actual GraphQL query which `Page.query` is coming from as a parameter. We are calling in the constant variable with the `useQuery` hook.

Back above the return statement, we have an object that contains the destructured fields queried which are the `title` and `description` from the data in WordPress `generalSettings`.

It co-locates the GraphQL query within the same file. You can also import and use queries from a file.

The benefit of Faust is that you don’t have to create the client object in your codebase. It is automatically created when you first import the `@faustwp-core` package. Instead, you can customize it by using a plugin filter.

## Custom plugin filter 🔌: Pagination example

For this article, let’s create a custom plugin filter to use cursor-based pagination in Faust.js with Apollo.  If you need a deeper understanding of pagination, please check out this article I wrote on [Pagination in Headless WP](https://developers.wpengine.com/blog/pagination-in-headless-wordpress-with-wpgraphql-apollo-next-js).

The first thing we need to do is go into the `components` folder at the root of the Faust project.  In the components folder, create a file called `LoadMorePost.js` and paste this code block in the file:

```
import { useQuery, gql } from "@apollo/client";
import Link from "next/link";

const GET_POSTS = gql`
  query getPosts($first: Int!, $after: String) {
    posts(first: $first, after: $after) {
      pageInfo {
        hasNextPage
        endCursor
      }
      edges {
        node {
          id
          databaseId
          title
          slug
        }
      }
    }
  }
`;

const BATCH_SIZE = 5;

export default function LoadMorePost() {
  const { data, loading, error, fetchMore } = useQuery(GET_POSTS, {
    variables: { first: BATCH_SIZE, after: null },
    
  });

  console.log(data);

  if (error) {
    return <p>Sorry, an error happened. Reload Please</p>;
  }

  if (!data && loading) {
    return <p>Loading...</p>;
  }

  if (!data?.posts.edges.length) {
    return <p>no posts have been published</p>;
  }

  const posts = data.posts.edges.map((edge) => edge.node);
  const haveMorePosts = Boolean(data?.posts?.pageInfo?.hasNextPage);

  return (
    <>
      <ul style={{ padding: "0" }}>
        {posts.map((post) => {
          const { databaseId, title, slug } = post;
          return (
            <li
              key={databaseId}
              style={{
                border: "2px solid #ededed",
                borderRadius: "10px",
                padding: "2rem",
                listStyle: "none",
                marginBottom: "1rem",
              }}
            >
              <Link href={`${slug}`}>{title}</Link>
            </li>
          );
        })}
      </ul>
      {haveMorePosts ? (
        <form
          method="post"
          onSubmit={(event) => {
            event.preventDefault();
            fetchMore({ variables: { after: data.posts.pageInfo.endCursor } });
          }}
        >
          <button type="submit" disabled={loading}>
            {loading ? "Loading..." : "Load more"}
          </button>
        </form>
      ) : (
        <p>✅ All posts loaded.</p>
      )}
    </>
  );
}

```
Let’s break this file down into chunks. At the top of the file, I am importing the `useQuery` hook and `gql` provided by the Apollo client that I am using as well as `next/link` from Next.js. We will need these imports in this file.

The next thing you see is the query we created with cursor-based pagination.

```
const GET_POSTS = gql`
  query getPosts($first: Int!, $after: String) {
    posts(first: $first, after: $after) {
      pageInfo {
        hasNextPage
        endCursor
      }
      edges {
        node {
          id
          databaseId
          title
          slug
        }
      }
    }
  }
`;

const BATCH_SIZE = 5;
```
After that, I have a default components function called `LoadMorePost`. In this function, I am utilizing the `useQuery` hook in Apollo to pass in my query called `GET_POSTS` from the top of the file.

Next, I have variables that I pass in, which is the `batch size` I defined to be `5` , and after null which tells the query to start from the beginning of the batch. This function gets fired off each time the user clicks the `“load more”` button.

```
export default function LoadMorePost() {
  const { data, loading, error, fetchMore } = useQuery(GET_POSTS, {
    variables: { first: BATCH_SIZE, after: null },
   
  });

  console.log(data);

  if (error) {
    return <p>Sorry, an error happened. Reload Please</p>;
  }

  if (!data && loading) {
    return <p>Loading...</p>;
  }

  if (!data?.posts.edges.length) {
    return <p>no posts have been published</p>;
  }

```

There are 2 variables that get set next. The first variable is `posts` which is taking the data that Apollo gives us back and drilling down into it with the `posts` and their nested data. The second variable is `haveMorePosts` which checks if we have more posts to load but if there are no more `posts` we will have to execute something else.

```
const posts = data.posts.edges.map((edge) => edge.node);
const haveMorePosts = Boolean(data?.posts?.pageInfo?.hasNextPage);

```

So now we can display our posts with a return statement with some data drilling within the levels of nesting that comes from the query.

Focusing now on the return statement, we have a `<ul>` tag. Within that tag, we are mapping over posts and returning a single post with a `databaseId`, `title`, and its `slug`. For each of those, we are displaying a list item with a `<li>` tag. This list item will have a title that has a link to the actual individual blog post’s page.

```
return (
    <>
      <ul style={{ padding: "0" }}>
        {posts.map((post) => {
          const { databaseId, title, slug } = post;
          return (
            <li
              key={databaseId}
              style={{
                border: "2px solid #ededed",
                borderRadius: "10px",
                padding: "2rem",
                listStyle: "none",
                marginBottom: "1rem",
              }}
            >
              <Link href={`${slug}`}>{title}</Link>
            </li>
          );
        })}
      </ul>
```
Lastly, we have to add a `“load more”` button. This button when clicked will load the next batch of posts from the cursor’s point. In order to do this, we take our `haveMorePosts` boolean and if we do have more, we will display a form with a button inside of it. When that button is clicked, we have a `onSubmit` handler that calls the `fetchMorefunction` in Apollo and passes in the variable called after that grabs the current end cursor, which is the unique ID that represents the last post in the data set to grab the next 5 after that end cursor.


```
  {haveMorePosts ? (
        <form
          method="post"
          onSubmit={(event) => {
            event.preventDefault();
            fetchMore({ variables: { after: data.posts.pageInfo.endCursor } });
          }}
        >
          <button type="submit" disabled={loading}>
            {loading ? "Loading..." : "Load more"}
          </button>
        </form>
      ) : (
        <p>✅ All posts loaded.</p>
      )}
    </>
  );
}
```
Now that we have created our component in Faust for a paginated page to load posts in batches of 5, the next step is to create a page to test this out.  Navigate to the pages directory in the root of the project and create a file called `pagination.js`.

In that file, copy and paste this code block in:

```
import Head from "next/head";

import LoadMorePost from "../components/LoadMorePost";

export default function LoadMore() {
  return (
    <>
      <Head>
        <title>Load More</title>
      </Head>

      <main>
        <h1>Load More Example</h1>
        <LoadMorePost />
      </main>
    </>
  );
}

```
In this file, we are importing the component into this file and exporting it as a default function, returning it to render on the page. 

### Custom Plugin Creation in Faust for the client object

The Apollo client can implement [relay-style pagination](https://relay.dev/docs/guides/graphql-server-specification/) with the relay spec using merge and read functions, which means all the details of `connections`, `edges` and `pageInfo` can be abstracted away, into a single, reusable helper function. WPGraphQL follows the relay spec as well.

What we need to do is create a plugin for relay-style pagination in order for Faust to utilize that [function from Apollo](https://www.apollographql.com/docs/react/pagination/cursor-based/#relay-style-cursor-pagination). 

In order to [create a Faust plugin](https://faustjs.org/docs/plugin-system/creating-a-plugin), we are going to use its `apply` method which is a JavaScript class. The `apply` method has a parameter called `hooks` which is passed from [@wordpress/hooks](https://www.npmjs.com/package/@wordpress/hooks).

The first step is to go to the root of the project and create a folder called `plugins`. In this plugin folder, create a file called `RelayStylePagination.js`.  Copy and paste this code block into that file:

```
import { relayStylePagination } from "@apollo/client/utilities";

export class RelayStylePagination {
  apply(hooks) {
    const { addFilter } = hooks;

    addFilter("apolloClientInMemoryCacheOptions", "faust", (options) => {
      return {
        ...options,
        typePolicies: {
          ...options.typePolicies,
          RootQuery: {
            ...options.typePolicies.RootQuery,
            fields: {
              posts: relayStylePagination(),
            },
          },
          ContentType: {
            fields: {
              contentNodes: relayStylePagination(),
            },
          },
        },
      };
    });
  }
}
```
At the top of the file, we import the `relayStylePagination` function from Apollo’s utility library.  Following that, we create a `class component` which is the basic syntax used in a Faust plugin. 

Next, we have an `apply` method with the `hooks` parameter which is an object that gives you a function called `addFilter`. [The `addFilter` function](https://faustjs.org/docs/plugin-system/filters) allows us to modify the Apollo Client’s `InMemoryCacheOptions`.

In the next few lines of code, we are taking the `addFilter` hook and calling the `memory cache` function `options`. The `options` are coming from the [Apollo Client Cache configuration](https://www.apollographql.com/docs/react/caching/cache-configuration/). These `options` allow for configuring the cache’s behavior to suit our use case. In this article, we are defining the configuration for pagination. The `faust` namespace follows that.

Next, we spread through our `options` which is a callback function that can return the `inMem cache options` as they are. We can also merge in our own with `typePolicies` that we define along with other specific queries we need to merge in the future. [The `typePolicies`](https://www.apollographql.com/docs/react/caching/cache-configuration/#typepolicy-fields) is a class that defines a policy of your type in Apollo. Here, we are adding it to the `RootQuery` option:

```
  addFilter("apolloClientInMemoryCacheOptions", "faust", (options) => {
      return {
        ...options,
        typePolicies: {
          ...options.typePolicies,
          RootQuery: {
            ...options.typePolicies.RootQuery
```

The last few lines of code are where we are defining our fields of the `posts` type. Once those are defined, we can now use the relay spec used by WPGraphQL to tell Apollo and its spec in our pagination method to go ahead and append the previous and next list together using cursor-based pagination by calling the `relayStylePagination` function provided. Another thing to note is I also added `ContentType` to expose the `contentNodes` fields in case of using something like search functionality.

```
   fields: {
              posts: relayStylePagination(),
            },
          },
          ContentType: {
            fields: {
              contentNodes: relayStylePagination(),
            },
          },
        },
      };
    });
  }
}
```

Lastly, we need to go to our `faust.config.js` file to imbed it into the `experimentalPlugins` key as a value like so:

```
import { setConfig } from "@faustwp/core";
import templates from "./wp-templates";
import possibleTypes from "./possibleTypes.json";
import { RelayStylePagination } from "./plugins/RelayStylePagination";

/**
 * @type {import('@faustwp/core').FaustConfig}
 **/
export default setConfig({
  templates,
  experimentalPlugins: [new RelayStylePagination()],
  possibleTypes,
});
```

### Generating possible types in Apollo

Before you run the dev server, make sure you type the command `npm run generate` since we updated the schema. The Apollo client requires that we provide a `possibleTypes` object that maps over our updated schema. The Faust framework provides the command script for you.

Stoked! Once you have added it to the config file in the plugins, this will allow you to have the pagination you expect when the user clicks your load more button and expects the next batch of posts!!


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ai3095hbt0exuukilk4g.png)


### Previous and Next Post Link extension of WPGraphQL

The last piece I want to cover is using the WP Template to your advantage to create components and where those GraphQL queries will live and co-locate.

Out of the box, WPGraphQL does not have the ability to expose and query for `previous` and `next` post. [The pagination fields](https://github.com/kellenmace/pagination-station/blob/main/pagination-fields.php) plugin will allow us to modify the GraphQL schema and accomplish this.

Once this plugin is downloaded into your WP admin, you can now query WPGraphQL for the links. The query I made looks like this which you can run in GraphiQL IDE with the `slug` as the query variable:

```
 query getPost($slug: ID!) {
    post(id: $slug, idType: SLUG) {
      databaseId
      title
      content
      slug
      previousPost {
        title
        slug
      }
      nextPost {
        title
        slug
      }
    }
  }
```

 I am querying for a single post via its slug as the variable. At the bottom of the query is where I am able to also query for the previous and next posts with the single post query as the starting point.

### Finalizing Previous and Next posts in Faust.js

Back in my faust.js app, navigate to the components directory at the root of the project. From there, go to the `Footer` folder and create a file called `PaginatedFooter.js` and copy this block and paste it in:

```
import Link from "next/link";

export default function PaginatedFooter({ post }) {
  const { previousPost, nextPost } = post;
  return (
    <>
      <footer style={{ display: "flex", textAlign: "center" }}>
        {previousPost ? (
          <div
            style={{
              border: "2px solid #ddd",
              padding: "1rem",
            }}
          >
            <Link href={`${previousPost.slug}`}>
              <a>👈 {previousPost.title}</a>
            </Link>
          </div>
        ) : null}
        {nextPost ? (
          <div
            style={{
              border: "2px solid #ddd",
              padding: "1rem",
              marginLeft: "1rem",
            }}
          >
            <Link href={`${nextPost.slug}`}>
              <a>{nextPost.title} 👉</a>
            </Link>
          </div>
        ) : null}
      </footer>
    </>
  );
}

```

At the top of the file, I am importing `Link` from `next/link` using Next.js’s client-side navigation to link pages.

Then I have a default function called `PaginatedFooter` that is accepting the `post` data as its props. The following is a constant variable that destructures the `post` props of `previousPost` and `nextPost` which we now have exposed and are querying from WPGraphQL.

```
export default function PaginatedFooter({ post }) {
  const { previousPost, nextPost } = post;

```

Lastly, I have a return statement wrapped in a fragment that will render a `footer` tag. In this `footer` tag, I have `previousPost`, and if the post does exist, we display that previous `post title`.

Using `next/link`, the user has access to a clickable link to route them to that previous post page. Otherwise, if we do not have a previous post, it will render `null`, and nothing will appear.

After that, we have a similar `JSX` called `nextPost` which does the exact same thing as `previousPost` except it will show and render the next post.

```
return (
    <>
      <footer style={{ display: "flex", textAlign: "center" }}>
        {previousPost ? (
          <div
            style={{
              border: "2px solid #ddd",
              padding: "1rem",
            }}
          >
            <Link href={`${previousPost.slug}`}>
              <a>👈 {previousPost.title}</a>
            </Link>
          </div>
        ) : null}
        {nextPost ? (
          <div
            style={{
              border: "2px solid #ddd",
              padding: "1rem",
              marginLeft: "1rem",
            }}
          >
            <Link href={`${nextPost.slug}`}>
              <a>{nextPost.title} 👉</a>
            </Link>
          </div>
        ) : null}
      </footer>
    </>
  );
}

```

This component is now ready to be embedded into a `single-page` WP template.

### Faust.js WordPress templates

Faust.js provides a JavaScript version of the [WordPress template hierarchy](https://wpshout.com/wordpress-template-hierarchy/) and its system. Let’s utilize this system for our paginated footer component with the `single.js` file which is the WP template component that renders the single-post details page.

Navigating to the `wp-templates/single.js`, copy and paste over the current boilerplate code already in this file with this block:

```
import { gql } from "@apollo/client";
import PaginatedFooter from "../components/Footer/PaginatedFooter";
import * as MENUS from "../constants/menus";
import { BlogInfoFragment } from "../fragments/GeneralSettings";
import {
  Header,
  Main,
  Container,
  EntryHeader,
  NavigationMenu,
  ContentWrapper,
  FeaturedImage,
  SEO,
} from "../components";

export default function Component(props) {
  // Loading state for previews
  if (props.loading) {
    return <>Loading...</>;
  }

  const { title: siteTitle, description: siteDescription } =
    props?.data?.generalSettings;
  const primaryMenu = props?.data?.headerMenuItems?.nodes ?? [];
  const footerMenu = props?.data?.footerMenuItems?.nodes ?? [];
  const { title, content, featuredImage, date, author } = props.data.post;

  return (
    <>
      <SEO
        title={siteTitle}
        description={siteDescription}
        imageUrl={featuredImage?.node?.sourceUrl}
      />
      <Header title={siteTitle} description={siteDescription} />
      <Main>
        <>
          <EntryHeader
            title={title}
            image={featuredImage?.node}
            date={date}
            author={author?.node?.name}
          />

          <Container>
            <ContentWrapper content={content} />
          </Container>
        </>
      </Main>
      <PaginatedFooter post={props.data.post} />
    </>
  );
}

Component.query = gql`
  ${BlogInfoFragment}
  ${NavigationMenu.fragments.entry}
  ${FeaturedImage.fragments.entry}
  query GetPost(
    $databaseId: ID!
    $headerLocation: MenuLocationEnum
    $footerLocation: MenuLocationEnum
    $asPreview: Boolean = false
  ) {
    post(id: $databaseId, idType: DATABASE_ID, asPreview: $asPreview) {
      title
      content
      date
      author {
        node {
          name
        }
      }
      previousPost {
        title
        slug
      }
      nextPost {
        title
        slug
      }
      ...FeaturedImageFragment
    }
    generalSettings {
      ...BlogInfoFragment
    }
    headerMenuItems: menuItems(where: { location: $headerLocation }) {
      nodes {
        ...NavigationMenuItemFragment
      }
    }
    footerMenuItems: menuItems(where: { location: $footerLocation }) {
      nodes {
        ...NavigationMenuItemFragment
      }
    }
  }
`;

Component.variables = ({ databaseId }, ctx) => {
  return {
    databaseId,
    headerLocation: MENUS.PRIMARY_LOCATION,
    footerLocation: MENUS.FOOTER_LOCATION,
    asPreview: ctx?.asPreview,
  };
};
```

There are 109 lines of code in this example so let’s break this down at a high level and then I shall focus on the changes I made to the actual boilerplate starter code with my own customization.



The templates in Faust as you see in the code block have 3 main parts: Component, Query, and Variable.  Please refer to the [Faust docs](https://faustjs.org/docs/templates#template-structure) to get a deeper explanation of what each does.


Let’s start by focusing on the query layer of the template.  At the bottom of the file, I have customized the query to ask for the `previousPost` and `nextPost` fields as shown:

```
Component.query = gql`
  ${BlogInfoFragment}
  ${NavigationMenu.fragments.entry}
  ${FeaturedImage.fragments.entry}
  query GetPost(
    $databaseId: ID!
    $headerLocation: MenuLocationEnum
    $footerLocation: MenuLocationEnum
    $asPreview: Boolean = false
  ) {
    post(id: $databaseId, idType: DATABASE_ID, asPreview: $asPreview) {
      title
      content
      date
      author {
        node {
          name
        }
      }
      previousPost {
        title
        slug
      }
      nextPost {
        title
        slug
      }

```

The variables are already set to what I need, so the last thing I have to do is go back up to the top of the component which is the rendering layer and add the `PaginatedFooter` component after importing it at the top within the return statement.  Then I can pass in and destructure the `post props` data in the `PaginatedFooter` component:

```
return (
    <>
      <SEO
        title={siteTitle}
        description={siteDescription}
        imageUrl={featuredImage?.node?.sourceUrl}
      />
      <Header title={siteTitle} description={siteDescription} />
      <Main>
        <>
          <EntryHeader
            title={title}
            image={featuredImage?.node}
            date={date}
            author={author?.node?.name}
          />

          <Container>
            <ContentWrapper content={content} />
          </Container>
        </>
      </Main>
      <PaginatedFooter post={props.data.post} />
    </>
  );


```

Super Stoked! First, run `npm run generate` for the types and then run this on the dev server. Let’s see this now work on the browser:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v12kmyzk4u1ki61ibn5u.png)


## Conclusion 🚀

Faust.js and its new GraphQL client Apollo give developers a better way to develop headless WordPress.

I hope you have a better understanding of how to work with Apollo in Faust.js. As always, super stoked to hear your feedback and any questions you might have on headless WordPress. Hit us up in our [discord](https://discord.com/invite/J2khkF9XYK)!