Skip to main content

Fetching data from Headless WordPress with Faust.js

NOTE: If you followed the instructions in the Getting Started with Next.js guide, you already have a working instance of the client. The following guide assumes you are setting up a client on an already existing application.

Faust.js uses GQty as the primary way to fetch data from Headless WordPress. GQty is a proxy-based GraphQL client. GQty preforms an invisible or skeleton render to identify what data is needed.

Setting Up Your Client#

GQty works primarily based on TypeScript typings that are generated using introspection on your GraphQL API. If you are using WPGraphQL as your Headless WordPress API, you can enable introspection and then generate typings for GQty. You will need to do the following:

  1. Run npm install -D @gqty/cli dotenv

  2. Create a generate script in your package.json that runs gqty generate

  3. Create a gqty.config.js file in the root of your project. Use the following example config:

    const dotenv = require("dotenv");
    dotenv.config({
    path: ".env.local",
    });
    /**
    * @type {import("@gqty/cli").GQtyConfig}
    */
    const config = {
    react: false,
    scalarTypes: { DateTime: "string" },
    introspection: {
    endpoint: `${
    process.env.NEXT_PUBLIC_WORDPRESS_URL ?? process.env.WORDPRESS_URL
    }/graphql`,
    headers: {},
    },
    destination: "./src/client/index.ts",
    subscriptions: false,
    javascriptOutput: false,
    };
    module.exports = config;
  4. Run npm run generate

If everything runs smoothly you will end up with an index.ts and schema.generated.ts file in your src/client directory. The index.ts file contains your client code and the schema.generated.ts file contains the typings for GQty. You can use the client as-is, but you will not get some of the benefits that Faust.js provides on top of the standard GQty client. In order to use Faust.js with your Headless WordPress API, you will need to add some additional functionality. Replace the contents of index.ts with the following:

/**
* GQTY: You can safely modify this file and Query Fetcher based on your needs
*/
import type { IncomingMessage } from "http";
import { getClient } from "@faustjs/next";
import {
generatedSchema,
scalarsEnumsHash,
GeneratedSchema,
SchemaObjectTypes,
SchemaObjectTypesNames,
} from "./schema.generated";
export const client = getClient<
GeneratedSchema,
SchemaObjectTypesNames,
SchemaObjectTypes
>({
schema: generatedSchema,
scalarsEnumsHash,
});
export function serverClient(req: IncomingMessage) {
return getClient<GeneratedSchema, SchemaObjectTypesNames, SchemaObjectTypes>({
schema: generatedSchema,
scalarsEnumsHash,
context: req,
});
}
export * from "./schema.generated";

The code above is a modified version of the default index.ts file that is generated by GQty. The getClient function is a helper function that returns a client that is configured to work with the Headless WordPress API. Note the additional serverClient function that is used to create a client that is configured to work with the server by passing in the IncomingMessage object. This allows Faust.js to read cookies on the server and pass them along to the Headless WordPress API.

Troubleshooting#

"GraphQL introspection is not allowed"#

If you run into the error message GraphQL introspection is not allowed, but the query contained __schema or __type, you will have to temporarily enable introspection.

Introspection is disabled by default in WPGraphQL. To enable it, go to WP Admin -> GraphQL -> Enable Public Introspection.

If you are using something other than WPGraphQL you will need to refer to the documentation on how to enable introspection.

Once the schema file has been generated, you can then disable introspection again.

Updating the GraphQL Schema Typings#

If you followed the steps above, or you started a project using the examples/next/getting-started boilerplate you will have a schema.generated.ts file in your src/client directory. The typings in this file are generated from the Headless WordPress API. If you are using a different Headless WordPress API or you add additional queries or mutations to your existing Headless WordPress API, you will need to update the typings in this file. Possible reasons you might need to generate a new typings file include:

  1. Adding a plugin to your WordPress site that adds additional queries
  2. Using plugins like Atlas Content Modeler that add additional queries based on custom content types you create

To do this you will need to run gqty generate again. Running gqty generate will update the typings in the schema.generated.ts file and leave the index.ts unchanged.

Providing the GQty Client to Faust.js#

Using the boilerplate client code will provide two different GQty clients that you can use depending upon whether you are on the client or server. However, you will still need to provide the client to Faust.js so that it can use it to fetch data. To do this you can use the HeadlessProvider component published by Faust.js, and provide it the GQty client you want to use. This is done in your _app.tsx file as follows:

src/pages/_app.tsx
import { headlessConfig } from "@faustjs/core";
import { HeadlessProvider } from "@faustjs/next";
import { client } from "client";
import type { AppProps } from "next/app";
headlessConfig({
wpUrl: process.env.WORDPRESS_URL || process.env.NEXT_PUBLIC_WORDPRESS_URL,
apiClientSecret: process.env.WP_HEADLESS_SECRET,
});
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<HeadlessProvider client={client} pageProps={pageProps}>
<Component {...pageProps} />
</HeadlessProvider>
</>
);
}

This will ensure Faust.js uses the correct client to make requests on your behalf.

Using the Client to Make Queries#

Assuming you have created a client using the Faust.js getClient function, you will be able to take advantage of many of the added features that Faust.js provides, and also the general features provided by GQty. You can read our hooks for fething data reference for examples on how to use some of the built-in hooks using the Faust.js client, but the client will support any query to your Headless WordPress API. Let's look at a few examples of how to use the client to make queries.

The useQuery Hook#

If you are not able to use one of the WordPress-specific hooks you can use the useQuery hook to make a query to the Headless WordPress API. This hook is useful for making any query supported by your Headless WordPress API. It essentially exposes your entire generated GQL schema to you for you to use what you need. For example, say you have a Header component and you want to fetch menu items from your "Primary" menu in WordPress. You could do so as follows:

src/components/Header.tsx
import React from "react";
import styles from "scss/components/Header.module.scss";
import Link from "next/link";
import { client, MenuLocationEnum } from "client";
interface Props {
title?: string;
description?: string;
}
function Header({ title = "My Site Title", description }: Props): JSX.Element {
const { menuItems } = client.useQuery();
const links = menuItems({
where: { location: MenuLocationEnum.PRIMARY },
}).nodes;
return (
<header>
<div className={styles.wrap}>
<div className={styles["title-wrap"]}>
<p className={styles["site-title"]}>
<Link href="/">
<a>{title}</a>
</Link>
</p>
{description && <p className={styles.description}>{description}</p>}
</div>
<div className={styles.menu}>
<ul>
{links?.map((link) => (
<li key={`${link.label}$-menu`}>
<Link href={link.url ?? ""}>
<a href={link.url}>{link.label}</a>
</Link>
</li>
))}
</ul>
</div>
</div>
</header>
);
}
export default Header;

The code above demonstrates how you can use the useQuery hook to make a query to the Headless WordPress API for menuItems, filter your menuItems to be only those for the PRIMARY menu, then use the results to render links in your Header. Notice there is no code regarding any server-side data fetching, but if your page is setup to use SSR or SSG you will not have any client-side queries.

The useMutation Hook#

While Faust.js does not provide any WordPress-specific hooks for mutations, it does provide the useMutation hook. This hook is useful for making any mutation supported by your Headless WordPress API. For example, if you have a form on your site that can be used by admins to submit posts, it might look something similar to the following:

src/components/Header.tsx
import React from "react";
import { client } from "client";
export interface FormData {
title: string;
content: string;
}
export function PostForm() {
const [submit, { isLoading, error }] = client.useMutation(
(mutation, { title, content }: FormData) => {
const result = mutation.createPost({
input: {
title: title,
content,
},
});
return result.post?.id;
}
);
const errorMessage = error?.message;
return (
<form
onSubmit={(e) => {
e.preventDefault();
const { postTitle, content } = e.currentTarget;
submit({
args: {
title: postTitle.value,
content: content.value,
},
});
}}
>
<input type="text" name="postTitle" placeholder="Title" />
<textarea name="content" placeholder="Content" />
<input disabled={isLoading} type="submit" value="Send" />
{errorMessage ? <p>Error: {errorMessage}</p> : null}
</form>
);
}

NOTE: The above code is not a complete example of how you would implement form submissions to your Headless WordPress API. It is intended to demonstrate how mutations work using the Faust.js client.

The code above uses useMutation combined with a form to create new posts by calling the WPGraphQL Headless WordPress API.