Builders https://wpengine.com/builders/ Reimagining the way we build with WordPress. Mon, 22 Apr 2024 17:09:40 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.2 https://wpengine.com/builders/wp-content/uploads/cropped-favicon-1-32x32.png Builders https://wpengine.com/builders/ 32 32 Headless WordPress and Next.js 14: Core Competencies in App Router https://wpengine.com/builders/headless-wordpress-and-next-js-14-core-competencies-in-app-router/ https://wpengine.com/builders/headless-wordpress-and-next-js-14-core-competencies-in-app-router/#respond Mon, 22 Apr 2024 17:09:39 +0000 https://wpengine.com/builders/?p=31543 The App Router in Next.js 14 allows you to use React’s latest features, such as Server Components and Streaming. Combining this with headless WordPress can be daunting, especially with the […]

The post Headless WordPress and Next.js 14: Core Competencies in App Router appeared first on Builders.

]]>
The App Router in Next.js 14 allows you to use React’s latest features, such as Server Components and Streaming. Combining this with headless WordPress can be daunting, especially with the latest features within the framework and how the rendering methodology fits together in certain use cases.

In this article, I will guide you through a series of code challenges. I encourage you to try these yourself and if you get stuck, the answers are in the GitHub repository. At the end of this article and code challenge, you will gain knowledge of the core features in Next.js 14 using headless WordPress.

Pre-Requisite Knowledge

To get the most out of this article, you should have a basic understanding of HTML, CSS, React, and Next.js. If you need to brush up on your React skills, check out the React Foundations Course, which will introduce you to the fundamentals.

If you need to brush up on basic Next.js App Router methodology, please refer to my article here before reading this article.

The addition of the App Router to Next.js and the advent of React Server Components have ushered in a significant paradigm shift. In the past, devs using Next.js may have considered whole pages to be either SSG(Statically Generated), SSR(Rendered on the Server), or ISR(Incremental Static Regeneration).

Now, however, they’re most often thinking of their apps in terms of:

  • “Where should this render?” (server, client, or both)
  • “When should this render?” (build time, runtime, or stream whenever the server sends the response).

By the end of this, you’ll end up with your own Next.js 14 project that contains these pages:

  • /static-rendering
  • /dynamic-rendering
  • /streaming
  • /client-side-rendering

(A side note: I made a Navbar just to make it easier to navigate between pages, as you will see in the repo. However, you do not have to make one as you follow along since I will not go over the creation of one and the Next.js Link component.)

Here is the link to the finished repository for this article:

https://github.com/Fran-A-Dev/devrel-nextjs14-corecomp

Nested layouts and Partial rendering

Nested layouts are a new feature in the App Router. Let’s start with it.

Create a root layout that applies to the entire app. It should render this markup:

<!DOCTYPE html>
<html>
<head>
 <meta charset="UTF-8" />
 <title>DevRel Headless WP Core Competencies</title>
</head>
<body>
 <!-- Site content goes here -->
</body>
</html>

Replace <!-- Site content goes here --> with the children passed in.

Following that, create /dashboard/purchases and /dashboard/account routes with a simple div element rendered on each.

Once you have those finished, create a layout that applies to all routes nested under the /dashboard/.

It should render a sidebar that contains <Link /> components that point to the Purchases (/dashboard/purchases) and Account (/dashboard/account) pages.

Remember, if you get stuck during these challenges, you can refer to the finished repo. Here are links to the specific docs for this section:

https://nextjs.org/learn/dashboard-app/creating-layouts-and-pages

https://nextjs.org/learn/dashboard-app/navigating-between-pages

Image Component

Images are an important part of any website. In Next.js, its Image component extends the HTML <img> tag with features for auto-image optimization using a variety of props and configurations.

In this section, let’s utilize the image component to render an AVIF. An Avif is a powerful open-source, free file format that encodes AV1 bitstreams in the High Efficiency Image File Format (HEIF) container.

First, copy this image into your project:

https://videomentions.com/mic-and-headphones.avif

Then, on the Account page, render this image at the top using Next.js’ Image component. Make sure you specify the image and height to guard against Cumulative Layout Shift (CLS).

Below the image, render details and summary elements that display the following questions and their answers (it’s up to you to answer them 😊):

  • Does a simple <img> tag get rendered, or something else?
  • Is the primary image still in AVIF format, or something else?
  • Are srcset and sizes attributes defined to reference other sizes of this image? If so, explain how they work to serve responsive images.
  • Does the image leverage native lazy loading?

Inspect the image markup that gets rendered on the front end and take note of what you see.

The docs to this section: https://nextjs.org/learn/dashboard-app/optimizing-fonts-images#why-optimize-images

You should have something that looks like this now:

Server Components and Data Loading

React Server Components (RSCs)are components that allow you to write UI that can be rendered and optionally cached on the server side.  In Next.js 14 and its new App Router, the rendering work is further divided by route segments to enable streaming and partial rendering. The data loading is also done on the server.

Static Rendering

In this section, let’s utilize an RSC that does the data loading and rendering on the server. The goal is to create a full static page that can be cached on a CDN in a server component.

First, create a new /static-rendering route segment in the root of the App directory. Inside this route segment, include the declaration for a server component.

Then, await a fetch call to the /graphql endpoint associated with your headless WP backend to get the most recent 10 blog posts. Make a request for the databaseId, title, date and excerpt. Use raw fetch API for this and define the WPGraphQL requests in a template literal.

With the data returned, render a list of posts to the page. For good HTML semantics, represent each post as a <article> tag.

Time-Based Revalidation

This fully static page poses a problem, though. When a new blog post is published, this page will be out-of-date; it’ll still show the same list of posts that existed at the time it was generated.

Please use the stale-while-revalidate method here or SWR. The page should be invalidated once every 30 minutes to display accurate, fresh data. You can also test this out by doing a 10-second interval, changing your data in WordPress, then hitting your page again, waiting 10 seconds, then hitting it again. It should look like this:

The time-based method is great, but what if you have a use case where you need up-to-the-minute, close-to-real-time data? Next.js does have a way to do this. On-demand Revalidation allows you to invalidate your data on demand by path.

If you would like to add this method to the file instead of time-based, please free to do so. I left this method out of the repo. Currently, Atlas does not support on-demand revalidation but is working on adopting support for it soon!

Here are the docs for this section:

Static Rendering: https://nextjs.org/learn/dashboard-app/static-and-dynamic-rendering

Revalidation Techniques: https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#revalidating-data

https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#on-demand-revalidation

Dynamic Rendering

Dynamic Rendering or Server-Side Rendering is content rendered on the server for each user when the user visits the page at each request. This rendering method is beneficial if you need real-time data, user-specific content such as a dashboard, or access to information that can only be known at request time, such as cookies.

In this section, we will utilize this method to display relative date strings on a page, such as “Ten minutes ago,” “Three months ago,” etc.

First, create a new /dynamic-rendering route segment in the App directory. Copy your page component from the /static-rendering route and paste it in as the starting point for the /dynamic-rendering page component.

Next, use the unstable_noStore utility to opt out of static rendering and make the page dynamic (rendered on the server at request time). Docs: https://nextjs.org/docs/app/api-reference/functions/unstable_noStore

Let’s use the date-fns Use the NPM package to format dates as relative date strings (“Ten minutes ago,” “Three months ago,” etc.), as shown in this example.

If you have followed these steps correctly, you have now created a dynamic page that will always display up-to-date relative date strings to users. Stoked! It should look like this:

There are a few considerations in this section. Dynamism is costly since it requires more load on the server and is less performant. This is no longer a cacheable static page. It will hit the server on each request. Sometimes, though, it is worth it, depending on your use case. The date-fns library will not be included in the client-side bundle since this is being used server side.

Streaming and React Suspense

Streaming is a data transfer technique that allows you to break down a route into smaller “chunks” and progressively stream them from the server to the client as they become ready.

In this section, let’s use streaming to grab data from WPGraphQL and a random API endpoint that is not from WordPress. Docs:https://nextjs.org/learn/dashboard-app/streaming

The first thing we need to do is choose a random API endpoint. You can use any API endpoint that returns JSON data. For this example, I am going to use JSON server, an NPM package that allows you to run a local server and gives you an endpoint on whatever port you want to watch it at.

If you decide to use the JSON server method, you will need to create a file to house the JSON. For this article, I created a folder in the root of the project and just added my JSON there:

(Just a reminder, in choosing this method, you will need to run the command on another terminal to spin up the JSON server)

If you do not feel like using that, you can use any common one. I find the Rick and Morty GraphQL API to be a reliable one that many developers use. With this method, you can fetch the data right from the endpoint.

 Next, create a new route segment called /streaming. Copy the /dynamic-rendering page component into it as a starting point.

Remove the data fetching from the page component and add these two functions to the file:

https://gist.github.com/Fran-A-Dev/678ebe8a4e0affb80ab253402dcb88bc

Notice the “Simulate 3 seconds of network latency” line within the fetchStarWarsCharacters() function. This will be the data that will lag behind the WordPress data. Because this network latency exists, let’s opt for a streaming approach where we’re going to render our page immediately, show a loading placeholder, and then stream in the markup from these components when they’re ready using React Suspense.

Once that is all done, create a custom loading.jsx file within the root of the application and add whatever markup and style you want to it. When you create that file, import it into the page component of the streaming route segment and pass that into the Suspense component like this: <Suspense fallback={<Loading />}>

Ensure that within the page.jsx for this component, the post list, and characters list pop into the UI at the same time, regardless of which one took longer.

Stoked!!! We now have a page that loads as quickly as possible with the parts of the page that rely on more expensive queries being streamed in. It should look like this:

Client-Side Rendering

Client Components allow you to write interactive UI that is prerendered on the server and can use client JavaScript to run in the browser. Now, this was the default rendering method in React.js before server components. However, with Next.js 14, you now have to declare a component to be client-side with the "use client" directive in a file.

For this section, let’s build a dynamic page that grabs the github-username query string parameter in the URL, fetches that user’s public SSH key, and displays it on the page. This data fetching and rendering will take place entirely on the client– none of it on the server.

First, create a new /client-side-rendering route segment. Inside that route segment, create a simple page component that renders, then renders the <GitHubSSHKey /> component that we’ll create next.

Now, within this same route segment, create a GitHubSSHKey component. Include the "use-client" directive to mark it as a client component. Inside of this component, render the custom loading component we made in the previous section. Here are the docs: https://nextjs.org/docs/app/building-your-application/rendering/client-components

Following that, we need to access the value of the github-username query string parameter as described in the docs here. We can use the value of the github-username to make a fetch request like this to grab the user’s SSH key:

const response = await fetch(`https://github.com/${username}.keys`);
const sshKey = await response.text();

Once the request resolves, store the SSH key in state and trigger a re-render to replace your loading skeleton with this:

<h2>Public SSH key for {username}:</h2>
<p>{sshKey}</p>

Now, just add a query parameter to the URL with the github-username key and assign it the username and SSH key you want to grab. I used Kellen Mace’s.

Stoked!!! We have now implemented a client component. Something to keep in mind is that you can render a client component from a server component which we just did here. However, the other way around will not work. You can’t have a client component that renders a server component.

Conclusion

Next.js 14 is the latest version of the most used meta framework on top of React. It introduces new ways to handle data, create routes and files as well as rendering methods.  We hope you have a deeper understanding of how its core competencies work together.

Stay tuned for more Next.js 14 and headless WordPress content coming soon!!

As always, stoked to hear your feedback and any questions you might have on headless WordPress! Hit us up in our Discord!

The post Headless WordPress and Next.js 14: Core Competencies in App Router appeared first on Builders.

]]>
https://wpengine.com/builders/headless-wordpress-and-next-js-14-core-competencies-in-app-router/feed/ 0
WPGraphQL Smart Cache Updates: Clarifying The Cache https://wpengine.com/builders/wpgraphql-smart-cache-updates-clarifying-the-cache/ https://wpengine.com/builders/wpgraphql-smart-cache-updates-clarifying-the-cache/#respond Fri, 05 Apr 2024 15:39:15 +0000 https://wpengine.com/builders/?p=31534 WPGraphQL Smart Cache is a plugin in the WPGraphQL ecosystem that provides on-demand invalidation of cached WPGraphQL data. This means that you can leverage caching solutions like a persistent object […]

The post WPGraphQL Smart Cache Updates: Clarifying The Cache appeared first on Builders.

]]>
WPGraphQL Smart Cache is a plugin in the WPGraphQL ecosystem that provides on-demand invalidation of cached WPGraphQL data. This means that you can leverage caching solutions like a persistent object cache or a network cache such as Varnish for great performance and automatically invalidate the cached data whenever data changes in WordPress to ensure that cached data stays fresh.

In this article, I will go over in detail the new updates to the plugin.

If you prefer video format, please reference the video here.

Prerequisites

To benefit from this article, you must have a fundamental understanding of headless WordPress, WPGraphQL, and Smart Cache.  

If you are not familiar with the basics and functionality of WPGraphQL Smart Cache, please refer to my article as well as the WPGraphQL article here.

You will also need a supported host since this requires a specific implementation within your hosting platform to work. WP Engine is one host that supports this out of the box. If you do not have a WP Engine server, you can get a free sandbox here.  

Jason Bahl also has a gist to make it work with Lightspeed hosts: https://gist.github.com/jasonbahl/d777c7229bad5142211a58ed00da6598

WPGraphQL Smart Cache

Two of the most difficult things in Web Development are naming things and cache invalidation.  Luckily, the creation of WPGraphQL Smart Cache has made it easier.  Since this plugin’s beginnings two years ago, it has some updates to go over. Let’s dive into them.

What’s New

The UI within WP Admin when downloading the plugin clarifies the settings more to improve the visual hierarchy and organization.


When you download the plugin, navigate over to the GraphQL > Settings > Cache page on the left-hand side of the hamburger menu and you should see this:

Network Cache

The first setting is the Network Cache.  Network cache refers to a system that stores copies of content close to users, typically within a network infrastructure like CDNs or web servers, to reduce latency and server load. When a user requests content, the network cache provides the data from the nearest cache node instead of fetching it from the origin server. This process speeds up content delivery, decreases the load on the origin server, and improves the overall user experience by providing quicker access to the content.

This setting influences how the plugin interacts with network cache clients like Varnish. For instance, the Cache-Control max-age setting specifies how long (in seconds) the network should consider the cached data fresh for GraphQL requests. By setting a proper max-age, the plugin leverages the built-in caching layers of the hosting environment, reducing the load on the WordPress server.

If you use a max-age of 0 , requests will not be cached. This could be necessary for scenarios where data must be real-time or near-real-time, and any caching could result in stale data being served to the client.

However, this should be used with caution for these reasons:

Performance Impact: Without caching, each request for data will hit the WordPress server directly, which can lead to increased load times for the client and higher resource utilization on the server. This defeats the primary benefit of caching, which is to improve performance by reducing the load on the server.

Scalability Concerns: In high-traffic scenarios, bypassing the cache could potentially overload the server with requests that could have been served from the cache, leading to scalability issues.

Increased Costs: More server resources to handle uncached requests can lead to increased operational costs, especially if you are using a hosting service that charges based on resource usage.

Object Cache

Object cache is a mechanism that stores the results of complex data queries in memory to be quickly retrieved on subsequent requests, without the need to re-query the database each time. This is particularly useful for dynamically generated content that does not change between user requests, as it can significantly reduce database load and improve website performance.

Object cache should ideally be considered as a secondary layer of caching when network cache cannot be used. Network cache serves a broader scope by caching entire HTTP responses, often at the edge closer to the user, and is more efficient for content that is shared across many users. Object cache, being closer to the application layer, is suited for pieces of data unique to the application logic that might not be as effectively cached by network layers.

In scenarios where the network cache like Varnish is not available or suitable—perhaps due to highly dynamic content that changes frequently or personalized content that is unique per user—object caching becomes beneficial. It offers granular control over how individual pieces of data are stored and retrieved. However, because it typically resides on the same server as the application, it does not provide the same scalability benefits as network caching and can increase the load on the server if not managed correctly. Hence, it’s recommended to leverage network caching first, where possible, to maximize the efficiency and scalability of content delivery.

The new settings here would allow users to enable object caching and define the time-to-live (TTL) for the cached objects, which means how long the data remains in the cache before it’s considered stale and refreshed.

Debugging

Developers are always stoked to have tools for easier debugging.  Now they are available in Smart Cache.

Log Purge Events

This specific setting within the debugging section gives users the option to track when and why cache purges happen. This could be critical for understanding patterns in cache invalidation and ensuring the cache behaves as expected.

Purge Now

This option provides a manual trigger for users to clear the cached GraphQL Queries. This feature is useful when changes have been made that should be reflected immediately, bypassing the default expiration time.

Did You Purge The Cache

This field confirms whether a cache purge has been successfully triggered and completed. Knowing the last purge timestamp can be helpful in coordinating cache invalidation with site updates or troubleshooting issues.

Conclusion

The WPGraphQL Smart Cache plugin is a tool that supports and solves cache invalidation and caching optimally for WPGraphQL queries. 

I touched on some updated features of the plugin. I encourage everyone to dive into the repo, as mentioned, to get their hands dirty and explore all the other awesome features in this plugin.

There are more changes and updates to come. So as always stay tuned! We also would love to hear your feedback, thoughts, and projects you are doing in Headless WordPress so hit us up in our Discord!

The post WPGraphQL Smart Cache Updates: Clarifying The Cache appeared first on Builders.

]]>
https://wpengine.com/builders/wpgraphql-smart-cache-updates-clarifying-the-cache/feed/ 0
Custom Content in Headless WordPress Using Advanced Custom Fields and WPGraphQL https://wpengine.com/builders/custom-content-in-headless-wordpress-using-advanced-custom-fields-and-wpgraphql/ https://wpengine.com/builders/custom-content-in-headless-wordpress-using-advanced-custom-fields-and-wpgraphql/#respond Wed, 27 Mar 2024 16:01:27 +0000 https://wpengine.com/builders/?p=31523 Creating custom content types is a prevalent part of many modern, content-rich headless WordPress sites. By defining custom content types, developers can structure their data in a way that best […]

The post Custom Content in Headless WordPress Using Advanced Custom Fields and WPGraphQL appeared first on Builders.

]]>
Creating custom content types is a prevalent part of many modern, content-rich headless WordPress sites. By defining custom content types, developers can structure their data in a way that best suits their specific requirements, ensuring consistency and efficiency in content management. On a headless WordPress site, this can be done easily using Advanced Custom Fields to design the content structure and WPGraphQL to query data.

Introducing Advanced Custom Fields (ACF)

While WordPress provides built-in support for standard content types such as posts and pages, Advanced Custom Fields (ACF) allows users to define custom content types and fields for WordPress websites. This plugin enables developers to visually create custom fields for posts, pages, custom post types, and taxonomy terms. These custom fields can store a wide range of data types, including text, images, files, and more, making it a popular tool for tailoring WordPress content to specific needs.

Begin by defining custom post types and fields using the ACF interface within the WordPress admin dashboard. You can create any type of custom post type and add a few fields to it. Then, add some example data in the WordPress admin.

Setting Up GraphQL in WordPress

To expose WordPress data via GraphQL, we’ll utilize the WPGraphQL plugin. WPGraphQL provides a schema and endpoints to query WordPress data using GraphQL syntax.

WPGraphQL serves as the bridge between WordPress and modern frontend frameworks by exposing WordPress data via a GraphQL API. With WPGraphQL, developers can query WordPress content with precision, retrieving only the data they need for a particular request.

We will need to install two plugins to access ACF data using GraphQL queries: the WPGraphQL plugin and the WPGraphQL for ACF plugin.

Accessing ACF Data with GraphQL Queries

Settings to Make Data Accessible

Notably, you will need to do two things to proceed with accessing ACF data. First, check the Enable Public Introspection box in GraphQL’s settings. Then, on each created Post Type’s page in ACF, click the slider for Advanced Configuration. Select the furthest right tab titled GraphQL and enable the Show in GraphQL slider.

GraphQL Queries

Let’s dive into using GraphQL queries to retrieve ACF data from WordPress.

For example, here is a query that fetches a list of products along with their ACF fields:

{
  products {
    edges {
      node {
        id
        title
        acfFields {
          productName
          productDescription
          productImage {
            sourceUrl
            altText
          }
        }
      }
    }
  }
}

Let’s break down what each part of the query is doing:

  1. products: This is the root field of the query, indicating that we want to retrieve data related to products.
  2. edges: This is a connection type used in WPGraphQL to represent a list of items with pagination information. It’s commonly used when querying data from a list or collection.
  3. node: Within each edge, there is a node representing a single product item. This is where the actual data for each product resides.
  4. id: This field requests the unique identifier (ID) of each product. IDs are typically used to uniquely identify each item in a collection.
  5. title: This field requests the title of each product. Titles are common pieces of information associated with content items in WordPress.
  6. acfFields: This field requests custom fields associated with each product. In this case, it’s specifically asking for fields created using the Advanced Custom Fields (ACF) plugin.
  7. productName, productDescription, productImage: These are custom fields defined in ACF for products. The query requests data for each of these fields, including the product name, description, and image.
  8. sourceUrl, altText: These are sub-fields of the productImage field, requesting the source URL (i.e., the URL of the image) and alt text (i.e., the alternative text for the image) respectively.

For your own queries, you can replace the values such as productName, productDescription, and productImage with your own ACF field names.

Integrating ACF Data in a React App

Once we have our GraphQL query, we can integrate it into a React web application. This React component fetches the product data using the previous GraphQL query and displays the list of products.

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

const GET_PRODUCTS = gql`
{
  products {
    edges {
      node {
        id
        title
        acfFields {
          productName
          productDescription
          productImage {
            sourceUrl
            altText
          }
        }
      }
    }
  }
}
`;

const ProductList = () => {
  const { loading, error, data } = useQuery(GET_PRODUCTS);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error :(</p>;

  return (
    <div>
    {data.products.edges.map(({ node }) => (
      <div key={node.id}>
        <h2>{node.title}</h2>
        <p>{node.acfFields.productName}</p>
        <p>{node.acfFields.productDescription}</p>
        <img src={node.acfFields.productImage.sourceUrl} alt={node.acfFields.productImage.altText} />
      </div>
    ))}
    </div>
  );
};

export default ProductList;

Let’s take a look at how this React component works.

GraphQL Query Definition

The GET_PRODUCTS constant defines a GraphQL query using the gql tag template literal. This query requests data for products, including their IDs, titles, and custom fields (productName, productDescription, and productImage), from the headless WordPress backend.

ProductList Component

Inside the component, the useQuery hook is used to execute the GraphQL query defined in GET_PRODUCTS. It returns an object containing loading, error, and data properties. If the query is still loading (loading is true), the component renders a loading message (<p>Loading...</p>). If there is an error during the query execution (error is not null), the component renders an error message (<p>Error :(</p>). If the query is successful and there are no errors, the component renders a list of products.

The list of products is rendered using the map function on data.products.edges, which represents an array of product nodes. For each product node, a <div> element is rendered with the product’s title, productName, productDescription, and productImage (including source URL and alt text) displayed.

Conclusion

Many headless WordPress sites require the ability to create and query for custom content types. Leveraging Advanced Custom Fields (ACF) for content customization and WPGraphQL for seamless data retrieval offers a powerful solution for building flexible and customizable websites.

The post Custom Content in Headless WordPress Using Advanced Custom Fields and WPGraphQL appeared first on Builders.

]]>
https://wpengine.com/builders/custom-content-in-headless-wordpress-using-advanced-custom-fields-and-wpgraphql/feed/ 0
Post Previews in the App Router with Faust.js for headless WordPress https://wpengine.com/builders/post-previews-in-the-app-router-with-faust-js/ https://wpengine.com/builders/post-previews-in-the-app-router-with-faust-js/#respond Fri, 15 Mar 2024 16:10:53 +0000 https://wpengine.com/builders/?p=31512 Post previews in WordPress allow users to view content exactly as it will appear on the live site before it is published. This feature is essential for authors, editors, and […]

The post Post Previews in the App Router with Faust.js for headless WordPress appeared first on Builders.

]]>
Post previews in WordPress allow users to view content exactly as it will appear on the live site before it is published. This feature is essential for authors, editors, and administrators to review and approve posts, ensuring that formatting, images, and layouts are correct and meet the intended design and content standards. Post previews are accessible through the WordPress editor, where a “Preview” button is available.  

Getting this to work in a headless setup is difficult.  Luckily, Faust.js does this out of the box.

This article delves into the mechanics of post previews within the experimental app router example project of Faust.js. We will explore the implementation of Post Previews in traditional WordPress, examine the integration with Faust.js’s App Router, and dissect the underlying processes within the Faust.js framework itself.   

Prerequisites

To fully comprehend and gain insight from this article, you should have a foundational understanding of the App Router, including its naming conventions and its directory and file hierarchy. If you are not yet familiar with these concepts, please reference my previous article on the subject before proceeding with this one.

Post Previews in Traditional WordPress

Before we discuss how Faust.js with the App Router does Post Previews, let’s take a look at WordPress’ traditional monolithic architecture approaches it under the hood:

Draft Saving: When you’re working on a post in WordPress, it automatically saves your changes as a draft. This draft is stored in the WordPress database in the wp_posts table, with a post status of ‘auto-draft’ or ‘draft’.

Preview Request: When you click the “Preview” button, WordPress initiates a request to generate a preview of the post. This request includes a query parameter (usually preview=true) that tells WordPress this is a preview request.

Post Revision Creation: To handle the preview, WordPress creates a post revision. This is a copy of your post at that moment, also stored in the wp_posts table, but with a post type of ‘revision’. This ensures that your current edits, even if not saved as a draft, are captured in this revision.

Preview Link Generation: WordPress generates a preview link that includes a nonce (a one-time use security token) to ensure the preview request is valid. This link points to the post URL but with additional query parameters that instruct WordPress to load the revision instead of the published content.

User Role and Permission Check: When the preview link is accessed, WordPress checks the user’s role and permissions to ensure they have the right to view the preview. This step is crucial for content security and integrity.

Rendering the Preview: If the user has the appropriate permissions, WordPress then loads the post revision data instead of the main post data. The site’s theme and styling are applied to this revision content, and it’s rendered in the user’s browser. This process involves the same template hierarchy and rendering engine as the live site, ensuring an accurate representation of how the post will look once published.

Non-Published Content Handling: It’s important to note that this preview mechanism allows users to see changes in real-time for unpublished (draft or pending) posts as well as changes to already published posts. For published posts, WordPress stores the changes as revisions without affecting the live version until those changes are explicitly published.

Security Measures: The nonce and permission checks play a crucial role in ensuring that only authorized users can access the post previews, protecting unpublished content from unauthorized access.

This engineering design enables WordPress users to securely preview their content, ensuring that only the intended changes are published to the live site. It provides a precise preview of how the content will be presented to the end-users.

Let’s dive into how Faust.js with the App Router replicates this experience and engineering in headless WordPress from a decoupled architecture.

Faust.js and App Router Support

The experimental app router example project from Faust.js is a boilerplate starter kit that includes a sample site showcasing the utility of functions such as getClient, getAuthClient, faustRouteHandler, loginAction, and logoutAction. This project can serve as a foundation for your future endeavors and as an informative reference for App Routing in headless WordPress. It is designed for those who are proficient with the command line, have fundamental knowledge of Faust.js and Next.js routing, and have an understanding of the basics of JavaScript, WordPress, and Bash.

Now, let’s focus on how it does Post Previews.

How Post Previews Work in Faust.js with the App Router

Remember, everything in the App Router in Faust.js defaults to being a server component unless otherwise specified with the ‘use client’ directive.  Let’s look at the Faust.js App Router project’s folders and files that make Post Previews work along with the Faust.js plugin necessary for this.

Environment Variables

The first action necessary to make post previews work in App Router is the Faust.js plugin’s secret key, 

To get the secret key, navigate to the ‘Add Plugins’ page in WP Admin and search for the Faust.js plugin.

Once that is installed and activated, when you hover over the settings option in the left-side hamburger menu, it will display a Faust option.  Click on that and you will see this page:

 It is on this page that you can grab your secret key and your front-end URL which in a development server case, it’s going to be off port 3000 on local host.  The last thing you need to get is your WordPress site URL.

Now, navigate over to your code editor. In the .env.local file that you created when spinning up the boilerplate, you can add those values to their keys like so:

Faust.js API Route Handler

Once you have your environment variables set, ensure that you have a directory in the root of the app folder called api.  In this api folder, you should have a nested folder called faust which will contain a file called [route].ts.  It should look like this:

What this code executes is the handling of the endpoints for the auth token and login/logout functionality.  

Authenticated Requests

Navigate to the dynamic route segment that is set by the slug as its parameter which is the [slug]/hasPreviewProps.ts file in the root of the app folder.  You should see this:

export function hasPreviewProps(props: any) {
  return props?.searchParams?.preview === 'true' && !!props?.searchParams?.p;
}

The function hasPreviewProps checks if the preview mode is activated and if there is a post ID present in the properties passed to it. It returns true only if both conditions are met: the preview parameter is set to ‘true’ and a post ID is specified. This is used to determine if a post preview should be displayed. If either condition is not fulfilled, the function returns false, indicating that the normal, non-preview content should be rendered. This helps in conditionally showing either the live post or its preview during development.

Now that we have a function to help check if previews are activated and present, let’s look at the page.tsx file in the same directory to see how authentication and rendering the preview on the content works.

import { getAuthClient, getClient } from '@faustwp/experimental-app-router';
import { gql } from '@apollo/client';
import { hasPreviewProps } from './hasPreviewProps';
import { PleaseLogin } from '@/components/please-login';

export default async function Page(props) {
  const isPreview = hasPreviewProps(props);
  const id = isPreview ? props.searchParams.p : props.params.slug;

  let client = isPreview ? await getAuthClient() : await getClient();

  if (!client) {
    return <PleaseLogin />;
  }

  const { data } = await client.query({
    query: gql`
      query GetContentNode(
        $id: ID!
        $idType: ContentNodeIdTypeEnum!
        $asPreview: Boolean!
      ) {
        contentNode(id: $id, idType: $idType, asPreview: $asPreview) {
          ... on NodeWithTitle {
            title
          }
          ... on NodeWithContentEditor {
            content
          }
          date
        }
      }
    `,
    variables: {
      id,
      idType: isPreview ? 'DATABASE_ID' : 'URI',
      asPreview: isPreview,
    },
  });

  return (
    <main>
      <h2>{data?.contentNode?.title}</h2>
      <div
        dangerouslySetInnerHTML={{ __html: data?.contentNode?.content ?? '' }}
      />
    </main>
  );
}


At the very top of the file, the necessary modules are imported, including functions for authentication and GraphQL queries, the utility function to check for preview props, and a component to prompt for login if needed.

Following our imports, we define an asynchronous Page function component that takes props as an argument.

Within this function, we have a preview check.  We use the hasPreviewProps helper function to determine if the page is in preview mode (isPreview) and sets the id based on whether it’s a preview or a published page, using query parameters (searchParams.p) for previews or URL parameters (params.slug) for published content.

After that, we initialize the GraphQL Client with authentication if it’s a preview with the getAuthClient function, otherwise it initializes a regular client with getClient. If the client can’t be initialized, it returns a <PleaseLogin /> component to prompt the user to log in.

export default async function Page(props) {
  const isPreview = hasPreviewProps(props);
  const id = isPreview ? props.searchParams.p : props.params.slug;

  let client = isPreview ? await getAuthClient() : await getClient();

  if (!client) {
    return <PleaseLogin />;
  }

Once that is done, we execute a WPGraphQL Query using the client.query from Apollo to fetch content by variables which are the id and the idType, and that differs based on whether it’s a preview or not. It asks for the title, content, and date of a content node.

const { data } = await client.query({
    query: gql`
      query GetContentNode(
        $id: ID!
        $idType: ContentNodeIdTypeEnum!
        $asPreview: Boolean!
      ) {
        contentNode(id: $id, idType: $idType, asPreview: $asPreview) {
          ... on NodeWithTitle {
            title
          }
          ... on NodeWithContentEditor {
            content
          }
          date
        }
      }
    `,
    variables: {
      id,
      idType: isPreview ? 'DATABASE_ID' : 'URI',
      asPreview: isPreview,
    },
  });

Finally, the component the main content of the page inside a <main> tag, using the data from the WPGraphQL query to populate the title and the content (using dangerouslySetInnerHTML for the content to render HTML).

return (
    <main>
      <h2>{data?.contentNode?.title}</h2>
      <div
        dangerouslySetInnerHTML={{ __html: data?.contentNode?.content ?? '' }}
      />
    </main>
  );

This setup allows the page to dynamically render the correct version of a post or page based on whether the user is requesting a preview or the live content.

Stoked!!!! Let’s see this work in action!

Conclusion

Faust.js with support for the App Router is a new version of the most used headless WP meta framework on top of Next.js 14. It introduces new ways to handle data, create routes and files as well as rendering methods.  Mix it with headless WordPress and WPGraphQL with Post Previews and you have an easy entry into its core functionality.   We hope you have a better understanding of how it all works together.

As always, stoked to hear your feedback and any questions you might have on headless WordPress! Hit us up in our Discord!

The post Post Previews in the App Router with Faust.js for headless WordPress appeared first on Builders.

]]>
https://wpengine.com/builders/post-previews-in-the-app-router-with-faust-js/feed/ 0
Beta Testing WordPress with Local Blueprints https://wpengine.com/builders/beta-testing-wordpress-local-blueprints/ https://wpengine.com/builders/beta-testing-wordpress-local-blueprints/#respond Tue, 27 Feb 2024 18:48:07 +0000 https://wpengine.com/builders/?p=4810 Start testing the latest WordPress beta quickly with Local Blueprints.

The post Beta Testing WordPress with Local Blueprints appeared first on Builders.

]]>
A new release is on the horizon! 🌅

As with each release, there are countless hours of testing to ensure the overall experience is bug-free and optimized. WordPress 6.5 is targeted to be released on March 26, 2024. Right now, you can help by testing.

Local is the go-to tool to create a WordPress sandbox and effortlessly develop WordPress sites locally, and with Local you can get testing in seconds. Here are a few options to get you started.

Check out this video or continue reading to learn about all the ways to get testing.

Option 1: WP-CLI + Local

If you already have an existing site in Local then you can just upgrade it to the latest beta with WP-CLI. Here is how:

  1. Right-click on your site and choose ‘Open site shell’, which will open your system’s terminal application and automatically launch WP-CLI.
  2. Once WP-CLI is launched then just run this command: wp core update --version=6.5-beta-3
Open WP-CLI in Local

Option 2: Local + WordPress Beta Tester plugin

If you already have a Local site then you can install the WordPress Beta Tester plugin and get the latest beta.

  1. Visit your WordPress dashboard’s Appearance > Plugins, and choose ‘Add New
  2. Search and install the WordPress Beta Tester plugin
  3. Once activated, visit the Tools > Beta Testing area and update the settings to get the latest beta (select the “Bleeding edge” channel and “Beta/RC Only” stream).
WordPress Beta Tester plugin settings screen

Option 3: Local Blueprint FTW!

Save a few clicks and just import our custom Local Blueprint, which comes with everything installed and ready for testing: WordPress Beta Tester plugin with WP 6.5 Beta 3 already installed and the default Twenty Twenty-Four theme activated.

Just click the download button below and drag and drop the downloaded WordPress-Beta-Tester_6.5-beta-3.zip into your Local app to spin up a new site and get testing!

Drag and drop Blueprint into Local

(Note: the super secret WordPress username and password for the Blueprint is admin.)

Reach out to @WPEBuilders and let us know how you’re using Local and what you’re testing in the latest WordPress 6.5 beta release.

The post Beta Testing WordPress with Local Blueprints appeared first on Builders.

]]>
https://wpengine.com/builders/beta-testing-wordpress-local-blueprints/feed/ 0
Understanding The Templating System in Faust.js https://wpengine.com/builders/understanding-the-templating-system-in-faust-js/ https://wpengine.com/builders/understanding-the-templating-system-in-faust-js/#respond Fri, 23 Feb 2024 17:24:38 +0000 https://wpengine.com/builders/?p=31504 Templates in Faust bring the power of the WordPress Template Hierarchy to your JavaScript frontend application. This article is a high-level guide to walk you through how templates are resolved […]

The post Understanding The Templating System in Faust.js appeared first on Builders.

]]>
Templates in Faust bring the power of the WordPress Template Hierarchy to your JavaScript frontend application. This article is a high-level guide to walk you through how templates are resolved and how to create your first templates using this system.

Prerequisites

To benefit from this article, you will need to have a basic understanding of Faust.js, headless WordPress, and WPGraphQL.

This article also assumes that you already have a WP install and a Faust.js app created and connected.  If you do not, please refer to the Faust.js documentation.

If you need a more basic tutorial, please reference this article on the subject matter here.

The Traditional WordPress Template Hierarchy

Before we dive into what the templating system is in Faust.js, let’s quickly go over what it mimics in traditional WordPress. 

The WordPress template hierarchy is a system that WordPress uses to decide which template files to use when generating a particular page on a WordPress site. This hierarchy is designed to provide a flexible and efficient method for determining the layout and structure of a page based on the type of content being presented.

When a visitor accesses a page on a WordPress site, WordPress runs a query to determine the type of content requested (e.g., a blog post, a specific page, a category archive, etc.). It then consults the template hierarchy to identify the most appropriate template file to use to display this content. The hierarchy is structured as a series of decisions WordPress makes, looking for template files in a specific order. If it doesn’t find a specific template file, it moves on to a more generic template until it finds a match.

The hierarchy starts with the most specific template files (like those for individual posts or pages) and moves towards more general templates (like archive or index templates) if the more specific templates are not present in the theme. This allows theme developers to create highly customized experiences for different types of content by creating specific template files, while also providing default templates that ensure content is always presented, even if no custom templates are available.

For instance, for a single blog post, WordPress will first look for a template specific to the post’s ID, then for a template for the post’s slug, followed by a template for the post type, and so on, down to more generic templates like single.php and finally index.php if no other template is found.

Here is an image of the template hierarchy:

The Templating System in Faust.js

Now that we understand what Faust.js is mimicking from WordPress in its templating system, let’s discuss how templates are resolved in it. 

The template resolver works by sending a preliminary GraphQL request (called the Seed Query) for the given URI in WordPress. The response includes data like the database ID, slug, content type, etc, which is then used to determine what the possible templates are for the given URI.  This is how traditional WordPress does it but in JavaScript! Stoked!!!

In the next few sections, we will dissect the folder and file structure of the default boilerplate template system.

The WP-Templates Directory

The Faust.js template system obeys the Next.js Pages Router method.  When you spin up the boilerplate npm package, you will see a directory in the root of the project called wp-templates.  You can name this anything you want, but out of the box, this name makes the most sense.   This is the folder that will house your Faust.js templates.

The Default Files

The files that come out of the box with the Faust.js npm package and the pages that they resolve and render are as follows:

category.js :  This resolves and renders the category archive page with the posts that are related within certain categories from the WordPress backend

front-page.js : This resolves and renders the front page of the Faust.js boilerplate that contains your general settings data as well as anything you want to add to the front-page template.  When first spun up, it just renders HTML that says this is a front page.

index.js: This is where your template files will be kept and sifted through for the system to go through what template is best to resolve and render.

page.js : This file resolves and renders any page post type in WordPress.

single.js:  This file will render any post from the post type menu. Any individual blog posts will resolve and render this template. 

tag.js : This file is the archive page for a specific page. For example, if you have a tag called “apple” it will resolve all of the posts tagged as “apple”.

Here is an image of the boilerplate folder with files:

Create Your First Custom Template

Now that we have an understanding of how the templating works in Faust.js, let’s create a custom template.

The first template we will make is a custom sample page template.  Navigate to the wp-templates directory at the root of your Faust.js project and create a file called sample-page.js.  Once the file is created, copy and paste this code onto the file:

import { gql } from "@apollo/client";

// The Component is required
export default function Component(props) {
  return (
    <>
      <h1>{props.data.page.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: props.data.page.content }} />
    </>
  );
}

Component.query = gql`
  query GetPageDataByURI($uri: ID!) {
    page(id: $uri, idType: URI) {
      title
      content
      slug
    }
  }
`;

Component.variables = (seedQuery, context) => {
  return {
    uri: seedQuery?.uri,
  };
};


Component.variables = (seedQuery, context) => {
  return {
    uri: seedQuery?.uri,
  };
};

Let’s break down what is happening in the code block.

At the top of the file, we import the gql tag from the Apollo Client to parse WPGraphQL queries.

Next, we define a functional component named Component that takes props as an argument.  This will be the default exported component.

Following that we return some JSX to render the HTML using the React escape hatch to render the content from a string.  We traverse that data path accurately to gain access to the content of the page from WordPress, in this case, just the page content and the page title.

Below that, we have our WPGraphQL queries the Component.query syntax from Faust to assign the query prop of the component.  We are querying for page data by a variable which is its URI.

Lastly, we need to add its variable component function.  We define that and assign it a variables property which is used to compute the variables of the WPGraphQL query based on the input parameters seedQuery and context.  

Then we return an object containing the variables for the query and access the URI by chaining the seedQuery.  

The Index File

Before we test this out and see if this renders correctly on the browser, the finishing step is to add and specify the template in our index.js file in the wp-templates directory.  

The index.js file is the most important file in the wp-templates directory when using Faust.js for headless WordPress. It acts as the control center, telling the system which template to use for showing content on the browser. Without setting up this file, the website won’t display pages correctly, if at all.

I like to think of it as a directory or map. It lists all the templates you have and matches them with what the user wants to see. This setup is vital for making sure the right template is picked for each page request. Without this file, the system is like a library without an index; you wouldn’t know where to find anything.

In the wp-templates folder, import your sample-page.js file at the top, then in the export object add the key identifier and value of the imported module like so:

Notice that we make the key ID string unique by calling it page-sample-page. If we do not, the page will conflict with the page.js file and render that instead. Make sure to make the key ID string unique in relation to the other files in your wp-templates folder.

 We can now test this out and see if it works.

Go to your WP Admin and navigate to the Pages option on the left-hand side of the hamburger menu:

Make a test sample page.  You can call this whatever you want.  I called it Test Faust.js Sample Page.  Click on the page link you just created to edit it:

In the URL field, make sure the permalink reflects the name of your file on the frontend.  In this case, it is sample-page.  This will tell Faust.js what template to resolve.

Finally, click the link to view the page and you should see this on the browser:

Conclusion

The template hierarchy in traditional WordPress is an important system to help the overall UI experience for users on a website.  In headless WordPress, Faust.js compliments that hierarchy to mimic it on the JavaScript frontend. I hope this gave you a better understanding of how the basics work on the Faust.js side.

Stay tuned for my next article on this matter where we will dive a little deeper and create custom templates in Faust.js with WPGraphQL for ACF.  

As always, stoked to hear your feedback and any questions you might have on headless WordPress! Hit us up in our Discord!

The post Understanding The Templating System in Faust.js appeared first on Builders.

]]>
https://wpengine.com/builders/understanding-the-templating-system-in-faust-js/feed/ 0
How to Set Up WordPress As A Headless CMS https://wpengine.com/builders/set-up-wordpress-as-a-headless-cms/ https://wpengine.com/builders/set-up-wordpress-as-a-headless-cms/#respond Thu, 15 Feb 2024 19:19:35 +0000 https://wpengine.com/builders/?p=31487 Headless WordPress is a website or omnichannel app that uses WordPress content, delivered by the REST API or GraphQL to frontends developed outside WordPress (e.g., Next.js, Nuxt.js, React, Vue). Before […]

The post How to Set Up WordPress As A Headless CMS appeared first on Builders.

]]>
Headless WordPress is a website or omnichannel app that uses WordPress content, delivered by the REST API or GraphQL to frontends developed outside WordPress (e.g., Next.js, Nuxt.js, React, Vue). Before you start building a frontend site that connects to your WordPress backend for content, you need to configure your WordPress site with some recommended plugins.

Installing Plugins

WPGraphQL

WPGraphQL is a plugin for WordPress that extends the WordPress API to support GraphQL requests. This plugin is highly recommended, as it makes it much easier to work with WordPress content. In most cases, you can use the default configuration for WPGraphQL, so installing the plugin is enough to get you started.

Advanced Custom Fields

Advanced Custom Fields (ACF) lets you take full control of your WordPress edit screens and custom field data. It supports a ton of different field types that you can attach to content types in WordPress. Assuming you are using WPGraphQL, you should also install the companion WPGraphQL For Advanced Custom Fields plugin. This companion plugin will let you extend the WPGraphQL API with ACF information. There is no configuration necessary before you can start working with ACF.

Faust.js

Faust.js provides a set of tools to make building frontend applications with WordPress as the headless CMS a pleasant experience for developers and publishers. The plugin helps with post previewing, authentication, fetching data, and more. It is used in conjunction with the Faust.js framework to build out the frontend of your headless site.

The Faust.js plugin requires some customization when you first install it. You can find the settings for the plugin in Settings → Faust. You will want to configure your Frontend site URL to be your frontend website’s root URL. For example, if you are running a Node site on your local machine on port 3000, your Front-end site URL should be “http://localhost:3000”. You will need to connect it with the frontend Faust.js app in a .env.local file.

After you install the plugins listed above, your WordPress site will be ready for headless development. While there are certainly more plugins you can use; these are the base plugins we recommend based on what we commonly use to build headless WordPress sites!

The post How to Set Up WordPress As A Headless CMS appeared first on Builders.

]]>
https://wpengine.com/builders/set-up-wordpress-as-a-headless-cms/feed/ 0
Understanding the Limitations: WPGraphQL for ACF, and Meta Queries Support https://wpengine.com/builders/understanding-the-limitations-wpgraphql-for-acf-and-meta-queries-support/ https://wpengine.com/builders/understanding-the-limitations-wpgraphql-for-acf-and-meta-queries-support/#respond Wed, 14 Feb 2024 14:15:55 +0000 https://wpengine.com/builders/?p=31482 In this article, we will take a look at Meta Queries in WPGraphQL and ACF. If you are not familiar with Meta Data in WordPress, it is essentially supplemental information […]

The post Understanding the Limitations: WPGraphQL for ACF, and Meta Queries Support appeared first on Builders.

]]>
In this article, we will take a look at Meta Queries in WPGraphQL and ACF. If you are not familiar with Meta Data in WordPress, it is essentially supplemental information about objects. In the case of WordPress, meta data is additional information associated with objects native to WordPress, such as posts, users, comments, terms, or custom post types and taxonomies registered to WordPress using tools like ACF.

Given the many-to-one relationship of metadata in WordPress, your options are fairly limitless. You can have as many meta fields per object as you wish, and you can store just about anything as meta.

Meta data is a powerful tool in WordPress development, as it allows for the extension of standard data structures to accommodate the specific needs of a website without altering core database schemas. This is particularly important in headless WordPress setups, where custom fields (post meta) can be exposed via WPGraphQL to be consumed by the frontend application.

The WordPress Database

While there is incredible utility in the flexibility of storing arbitrary data to supplement WordPress objects, the flexibility can come at a cost.

In order to understand the issue that querying by meta presents, let’s take a look at the WordPress database.

WordPress relies on a MySQL database, a robust and widely-used relational database management system. This database stores all the content and settings of a WordPress site, structured into several tables that handle posts, pages, comments, users, and meta-information, among others. The core of content storage lies within the wp_posts table, which houses not just posts but also pages, custom post types, and attachments. Each entry in this table is uniquely identified by an ID column, serving as the primary key. This ID is auto-incremented for each new entry, ensuring uniqueness and facilitating efficient data retrieval.

The efficiency of querying the WordPress database, particularly when fetching posts by their ID, is largely due to how SQL databases are designed to index primary keys. When a query requests a post by its ID, the database’s indexing system allows for a rapid, direct access path to the sought-after record, significantly reducing the time and computational resources needed compared to scanning the entire table.

For reference, here is an image of the wp_posts data table:

The Meta Query Issue in WP Databases

Meta Queries in WordPress provide a way for querying posts based on meta data, but they can introduce significant performance challenges, particularly on large-scale sites like newspapers or media outlets, where meta data can amass to millions of rows. Each meta key-value pair in WordPress is stored in the wp_postmeta table, which has a flexible structure allowing for virtually any type of data to be stored. This flexibility comes with a cost. Since meta keys are not strictly defined and meta values can contain anything from JSON strings to URLs, there is no practical way to enforce indexing strategies that would normally optimize query performance.  It is like the wild-west. 🤠

As a result, when a Meta Query is executed, it will scan of all the rows in the wp_postmeta table, which is incredibly inefficient. This is like trying to find a needle in a haystack; the query must sift through potentially millions of rows to locate the desired records. In the context of a high-traffic site with extensive meta data, this can lead to slow query execution times and a heavy load on the database server, ultimately impacting the user experience and shutting the site down.

This is the major reason why this functionality is not part of the core WPGraphQL plugin.

Here is an image of a sample meta query table:

Now, this is just a normal demo site with not much post data.  But imagine a local or national media or newspaper site with millions of rows of meta.

Performance Degradation in Meta Queries

When considering that these operations might not be merely occasional but could be a regular part of the site’s operation, it becomes clear that Meta Queries can become a bottleneck for database performance which leads to slow queries and bad user experience.

Solutions to the Meta Query Issue

The first way to address the issue is to simply accept it. You could choose to overlook the performance impact and accept the slower query execution. For most WordPress sites, which are small enough, this may be so inconsequential that it doesn’t significantly affect your experience.

Utility Taxonomies

Leveraging a private taxonomy instead of post meta can lead to substantial performance improvements in headless WordPress setups. By tagging posts with a non-public taxonomy, you can sidestep the costly operations associated with meta queries.

For example:

new WP_Query([
  'post_type' => 'post',
  'tax_query' => [
     [
        'taxonomy' => 'text_field_utility_tax',
        'field' => 'title',
        'terms' => $search_term,
        'operator' => 'IN',
     ]
  ]
]);

Whenever the “text_field” is updated, it is synced to the text_field_utility_tax and associated with the post.  Then users could query via tax query instead of meta_query to avoid performance issues.

With this utility taxonomy in place, searching and organizing posts as well as UI’s for ACF fields becomes a taxonomy query, which is inherently more efficient than querying for wild meta-keys. 

The values under the hood perform better when a query is filtered via tax query. 

You can read more about the concept of Utility Taxonomies here: https://tomjn.com/2018/03/16/utility-taxonomies/

Other Solutions

Another option to filter data by Meta Values in WPGraphQL would be to write your own custom code to handle this. This would require registering field(s) to the Schema to allow input from the query, then filtering the resolver(s) to use that input and pass it to the underlying WP_Query (or equivalent) to filter the data being queried for.

There is also an extension WPGraphQL Meta Query that adds the ability to filter connections via meta queries.  Use these both at your own discretion and risk.  The WPGraphQL Meta Query extension is not actively maintained nor are there current plans to introduce this functionality to core WPGraphQL.

Conclusion

When developing apps and sites with large amounts of meta data in a headless WordPress environment using ACF and WPGraphQL, it is crucial to be aware of and understand the issues related to Meta Queries. I hope this article has provided you with a clearer understanding of the causes of these issues and how you can address them, albeit at your own risk.

Here are a few more links if you are interested in reading more about the subject:

https://tomjn.com/2017/02/27/not-post-meta-bad/

https://tomjn.com/2016/12/05/post-meta-abuse/

As always, stoked to hear your feedback and any questions you might have on headless WordPress! Hit us up in our Discord!

The post Understanding the Limitations: WPGraphQL for ACF, and Meta Queries Support appeared first on Builders.

]]>
https://wpengine.com/builders/understanding-the-limitations-wpgraphql-for-acf-and-meta-queries-support/feed/ 0
It’s Time To Upgrade From ACM https://wpengine.com/builders/its-time-to-upgrade-from-acm/ https://wpengine.com/builders/its-time-to-upgrade-from-acm/#respond Fri, 26 Jan 2024 20:43:31 +0000 https://wpengine.com/builders/?p=31472 For builders creating headless WordPress sites, modeling the data structure is a crucial part of the site development process. It’s one of the reasons we developed the Atlas Content Modeler […]

The post It’s Time To Upgrade From ACM appeared first on Builders.

]]>
For builders creating headless WordPress sites, modeling the data structure is a crucial part of the site development process. It’s one of the reasons we developed the Atlas Content Modeler (ACM) plugin to help power our Atlas platform.


Since WP Engine acquired the Delicious Brains plugins, the team have been working hard to make sure Advanced Custom Fields (ACF) is the most comprehensive and powerful solution available for building complex data structures in WordPress. With the release of ACF 6.1 in April of 2023, you can not only use the plugin to add custom fields, but also add and configure custom post types and custom taxonomies, making ACF the complete content modeling solution for WordPress sites.

Due to the advancement of ACF, ACM is entering an end-of-life phase. During this phase, we will continue to support the ACM plugin to ensure it is secure and functional, giving you time to move your site to our recommended replacement. While security support and critical bug fixes will continue to be provided through 2024, no new feature development will happen in ACM.

We recommend ACF as a replacement for Atlas Content Modeler. ACF is a mature and stable product, with a large community, and it has most of the features that Atlas Content Modeler has, and more!

Will WP Engine continue to invest in ACF?

Absolutely! WP Engine is continuing its investment in ACF with new features and more throughout 2024 and beyond.

ACF is an important plugin in the WordPress ecosystem and has become an integral part of the WP Engine family of products. The decision to retire ACM in favor of ACF reflects this investment as it will allow us to continue to ensure ACF is a best-in-class solution for building WordPress sites of all shapes and sizes.

Do I have to stop using ACM before the end of this year?

It takes a long time to develop a WordPress site and we understand that so, no, you do not have to stop using ACM today. Instead, we encourage you to start any new projects in ACF and make plans to migrate from ACM to ACF for existing projects in 2024.

Take a look at the 2023 recap from the ACF team to find out more.

Do I have to stop using ACM before the end of this year?

It takes a long time to develop a WordPress site and we understand that so, no, you do not have to stop using ACM today. Instead we encourage you to start any new projects in ACF and make plans to migrate from ACM to ACF for existing projects in 2024.

ACM will continue to receive critical bug fixes and security updates throughout 2024 to make this transition easier. As you get started in migrating existing projects we invite you to take a look at our ACM to ACF migration guide and reach out through our support channels if you have any questions.

ACF is a proven and robust tool for WordPress projects of all sizes and we look forward to helping you improve all your sites with ACF throughout 2024 and beyond.

The post It’s Time To Upgrade From ACM appeared first on Builders.

]]>
https://wpengine.com/builders/its-time-to-upgrade-from-acm/feed/ 0
Cookie Policy Popups in headless WordPress with Next.js 14 and WPGraphQL 🍪 https://wpengine.com/builders/cookie-policy-popups-in-headless-wp/ https://wpengine.com/builders/cookie-policy-popups-in-headless-wp/#respond Mon, 22 Jan 2024 17:03:21 +0000 https://wpengine.com/builders/?p=31457 Cookie policies are guidelines and rules that govern how cookies are used on websites to respect user privacy and comply with legal regulations. In the context of headless WordPress, which […]

The post Cookie Policy Popups in headless WordPress with Next.js 14 and WPGraphQL 🍪 appeared first on Builders.

]]>
Cookie policies are guidelines and rules that govern how cookies are used on websites to respect user privacy and comply with legal regulations. In the context of headless WordPress, which involves a decoupled architecture where the frontend is separate from the WordPress backend, implementing cookie policies and a popup banner involves a few considerations and steps.

In this article, we will take a look at how to implement cookie policy popups in headless using Next.js 13/14 with two methods. The first one will only use the cookies-next npm package with full customization and WPGraphQL.  The second will use a 3rd party service called Cookiebot.

Prerequisites

To benefit from this article, you will need to have a basic understanding of Next.js 14, headless WordPress, WPGraphQL, Tailwind styles, and 3rd party script services.

If you need a more basic tutorial, please reference my article on the subject matter here.

Before moving forward, we will be looking at the files in this example that I made here in my GitHub repo.  If you would rather spin up your own and just copy and paste the code blocks into yours, feel free to do so as well.

Laws like the EU’s General Data Protection Regulation (GDPR) and the California Consumer Privacy Act (CCPA) have strict rules about cookie usage. They require clear consent from users before any non-essential cookies are stored on their devices.

Websites must inform users about the cookies they use, what data they track, and obtain their consent. This is typically done through a cookie consent banner.

There are essential cookies (necessary for website functionality) and non-essential cookies (like those for analytics and advertising). Only essential cookies can be set without explicit consent.

Let’s take a look at how users can consent with cookies and implement a cookie consent banner on our front-end Next.js 14 application.

Method 1:  WPGraphQL and The Cookies Next NPM Package

Let’s take a look at how users can consent with cookies and implement a cookie consent banner on our front-end Next.js 14 application without a 3rd party service.

Client-Side Cookie Components 

In this section, let’s make a basic cookie consent banner that pops up when a user visits our site for the first time.  This banner will block the user from interacting with the site until they accept the terms of usage, privacy policy, etc. 

I have a basic headless WordPress site on Next.js 14 already spun up that shows my first 5 blog posts.  Let’s take a look at the app/components/CookieConsent.jsx file:

"use client";

import { useState, useEffect } from "react";
import { hasCookie, setCookie } from "cookies-next";
import Link from "next/link";
export default function CookieConsent() {
  const [showConsent, setShowConsent] = useState(true);

  useEffect(() => {
    setShowConsent(hasCookie("localConsent"));
  }, []);

  const acceptCookie = () => {
    setShowConsent(true);
    setCookie("localConsent", "true", {});
  };

  if (showConsent) {
    return null;
  }

  return (
    <div className="fixed inset-0 bg-slate-700 bg-opacity-70 z-[5000]">
      <div className=" fixed-bottom-0 left-0 flex items-center justify-between px-4 py-8 bg-orange-400">
        <span className="text-dark text-base mr-16">
          This site uses cookies to improve the UX. By using our site you
          consent to all the TOU in accordance with our{" "}
          <Link href="/privacy">Privacy Policy</Link>
        </span>
        <button
          className="bg-blue-700 py-2 px-10 rounded "
          onClick={() => acceptCookie()}
        >
          Accept
        </button>
      </div>
    </div>
  );
}

At the top of the file, we declare the “use client” directive since we are going to be doing this on the client side.  This is followed by imports that we will use in this file which are the useState and useEffect React hooks to manage state and lifecycle.  The hasCookie and setCookie are functions we import from the cookies-next npm package we installed.  As well as the Link component for client-side navigation from Next.js.

After the import statements, we define the component and make it the default export of the module.  We initialize the state variable showConsent with a default value of true which determines whether the consent banner should be displayed.

Following that, we run the useEffect hook to perform the side effect to check for the presence of a cookie named localConsent when the component mounts.  If the cookie is there, it sets showConsent to false which prevents the banner from popping up.

Next, we define a function that will be called when the user clicks the “Accept” button on the banner.  It sets showConsent to false to hide the banner and sets a cookie named localConsent to true indicating that consent has been given.

Then, we have an if statement that says if showConsent is true, the component will render nothing and hide the banner.

Lastly, the return statement renders the JSX and the actual banner on the screen with the onClick handler that accepts the cookie when clicked.

To sum this up, the CookieConsent component uses local state and effect hooks to manage the display of a cookie consent banner based on the presence of a consent cookie. It provides a message about cookie usage and allows the user to accept the terms, which should hide the banner and set a cookie to remember the choice.

When you visit localhost:3000 after running the dev server, you should see this banner popup as a new user:

Create the Declaration and Policy Page with WPGraphQL

Now, let’s get stoked and take a look at using WPGraphQL to get the Policy Page data from WordPress into Next.js.  Go to app/pricvacy-policywp/page.jsx:

import { Suspense } from "react";
import Loading from "../loading";

async function getPage() {
  const query = `
    query GetPrivacyPolicyPage {
      page(id: "/privacy-policy/", idType: URI) {
        title
        content
      }
    }
  `;

  const res = await fetch(
    `${process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT}?query=${encodeURIComponent(
      query
    )}`,
    {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
      },
      cache: "no-store",
    }
  );

  const responseBody = await res.json();

  if (responseBody && responseBody.data && responseBody.data.page) {
    return responseBody.data.page;
  } else {
    throw new Error("Failed to fetch the privacy policy page");
  }
}

export default async function PrivacyPolicy() {
  const page = await getPage();

  return (
    <main>
      <nav>
        <h1>{page.title}</h1>
      </nav>

      <Suspense fallback={<Loading />}>
        <div className="card">
          <p dangerouslySetInnerHTML={{ __html: page.content }} />
        </div>
      </Suspense>
    </main>
  );
}


We have a server component with our imports Suspense and LoadingSuspense comes from React and allows other components to wait for something before rendering.  It handles the loading state of async operations like data fetching.  Loading is our custom component that serves as the placeholder to let the user know an operation is in progress.

Following that, we have an async function that defines the WPGraphQL query to fetch the title and content of a page with the URI “/privacy-policy/” hard coded since this is the only page we want to route to.

We use the fetch API to make a GET request to the WPGraphQL endpoint in our environment variable.  The query is URL encoded to ensure it’s transmitted correctly over HTTP.

After the request, the JSON is parsed and checks if the data is present.

Then we have an async component called getPage that fetches the data by calling getPage when rendered with some basic error handling to throw an error if the data is not present.

 Then we return it with the JSX and its format on the page.  You should see this when you visit localhost:3000/privacy-policywp after running the dev server:

Things to Consider When Implementing Without a 3rd Party Service

Choosing this method does give you full control and customization on creating your banner and policy page with no cost for a 3rd party service.  However, you do have a bit more complexity in keeping up with compliance, testing the functionality, and no auto cookie scanning.

In this section, let’s make a cookie consent banner in our Next.js 14 app and offload it to a 3rd party service called Cookiebot.

Cookiebot

Cookiebot is a 3rd party service that supports website compliance with privacy laws like the GDPR, ePR, and CCPA. It offers an automated platform that can handle user consent, cookie monitoring, and cookie control. Cookiebot enhances websites by providing a consent banner and tracking technology that informs visitors about the use of cookies, secures their consent, and honors their privacy preferences.

By integrating with our headless website, it automatically scans and tracks all cookies and similar tracking technologies in use, providing detailed reports and ensuring that the website’s use of such technologies remains transparent and compliant with legislation. 

It also helps in maintaining an updated cookie policy and declaration, automatically reflecting any changes in the use of cookies on the website.  

When using this approach, a few factors to consider are the cost of the service, limited customization options, and integration complexity.

Cookiebot Scripts in Next.js 14

Let’s take a look at the files and scripts we will need to make this work in our application.  (Just a note, I have already created a free-tier Cookiebot account.  If you are following along step-by-step, please do so before moving forward) 

In your Cookiebot Admin UI, go to Dashboard > Configuration and you should see this screen:

This allows you to set up the UI of the banner.  We will use the default setting to show on our Next.js front end in this example.

The next page you will need to navigate to is Dashboard > Script Embeds.  You should see this page:

We will need both script tags to embed in our Next.js 14 components.

Now, let’s take a look at our first component that dynamically inserts our Cookiebot script into our site at app/components/CookieBotConsent.jsx:

"use client";

import { useEffect } from "react";

const CookieConsent = () => {
  useEffect(() => {
    const cmpScript = document.createElement("script");
    cmpScript.id = "Cookiebot";
    cmpScript.src = "https://consent.cookiebot.com/uc.js";
    cmpScript.setAttribute("data-cbid", "your-cookiebot-id");
    cmpScript.setAttribute("type", "text/javascript");
    cmpScript.setAttribute("async", "true");
    cmpScript.setAttribute("data-blockingmode", "auto");
    document.head.appendChild(cmpScript);

    return () => {
      document.head.removeChild(cmpScript);
    };
  }, []);

  return null;
};

export default CookieConsent;

This file is very similar to the first section’s component as we are going to use the client-side directive for this as well as the useEffect hook from React.

We use the useEffect hook to execute side effects in the component and the empty dependency array ensures that the effect runs only once after the initial render.

The focus in this component is on the script elements.  Inside our useEffect hook, we have a variable that creates a new script element called cmpScript.  This is where we load the Cookiebot script.

Then we set all the attributes.  I won’t go over every attribute.  The main ones I will focus on are:

cmpScript.src = "https://consent.cookiebot.com/uc.js"; – This specifies the source of the script, pointing to the Cookiebot script.

cmpScript.setAttribute("data-cbid", "your-cookiebot-id"); – This sets your specific Cookiebot ID. Replace "your-cookiebot-id" with your actual Cookiebot ID.

cmpScript.setAttribute("data-blockingmode", "auto"); – This sets the blocking mode for scripts to "auto", allowing Cookiebot to manage script blocking based on user consent.

Following the running of those scripts, we append the element to the head of the document which injects the Cookiebot script into the page.

Then we run a cleanup function that removes the script element from the document head when the component unmounts.  This will prevent any unintended side effects.

We are not rendering any JSX here as we are using Cookiebot to show a UI, so we return null.

The next step is to add the CookieBotConsent component to our global layout so that it appears on all pages of our site.  In order to do this, we go to app/layout.jsx:

import "./globals.css";
import { Rubik } from "next/font/google";
import CookieBotConsent from "./components/CookieBotConsent";
import Navbar from "./components/Navbar";

const rubik = Rubik({ subsets: ["latin"] });

export const metadata = {
  title: "Headless WordPress Example",
  description: "Generated by Franly the Manly",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body className={rubik.className}>
        <Navbar />
        {children}
        <CookieBotConsent />
        <footer
          style={{ textAlign: "center", marginTop: "20px", fontSize: "14px" }}
        >
          © 2024 Fran Agulto Dev Stoke
        </footer>
      </body>
    </html>
  );
}


The CookiebotConsent component is added under the Navbar, right before the footer.  Now, this will appear on all our pages in the entire application.

To effectively test the Cookiebot consent banner, we have a few options. However, a key requirement is a publicly accessible domain, as well as ensuring cross-domain consistency within a live production environment. It’s important to note that using localhost:3000 from the Next.js development server is not a viable option for this testing.

The 3 ways you can test the banner are:

  1. Get a free Atlas sandbox account or any host, spin up a live site with a URL 
  2. Use the preview link feature off Atlas on a feature branch
  3. Use a service like ngrok to tunnel a public domain to the localhost:3000 port

(Please refer to the documentation if you choose the ngrok setup)

Once you have chosen your method, go to your Cookiebot admin UI and navigate to Dashboard > Domains and Aliases.  You should see this page and once you click the button to add a domain, you are connected.

We now have a publicly accessible URL that Cookiebot can track and if I visit the site for the first time, the Cookie Consent banner pops up like so:

Create the Declaration and Privacy Policy Page

The next component we will look at is a page that renders the Privacy Policy from Cookiebot. Navigate to app/components/CookieBotDeclaration.jsx:


"use client";
import { useEffect } from "react";

const CookieDeclaration = () => {
  useEffect(() => {
    const cookieDeclarationScript = document.createElement("script");
    cookieDeclarationScript.id = "CookieDeclaration";
    cookieDeclarationScript.src =
      "https://consent.cookiebot.com/your-cookiebot-id/cd.js";
    cookieDeclarationScript.setAttribute("type", "text/javascript");
    cookieDeclarationScript.setAttribute("async", "true");
    document.body.appendChild(cookieDeclarationScript);

    return () => {
      document.body.removeChild(cookieDeclarationScript);
    };
  }, []);

  return null; // The script itself handles rendering the cookie declaration
};

export default CookieDeclaration;

I am not going to go over the entire code block because this is identical  to the component we looked at that ran the CookieBotConsent.jsx file.  The difference is that we are just running the script to render the Privacy Policy and Declaration page that comes from it.

Lastly, we need to insert the component into our privacy-policy route segment folder which has the page.jsx file like so:

import CookieBotDeclaration from "../components/CookieBotDeclaration";
import React from "react";

const PrivacyPolicyPage = () => {
  return (
    <div>
      <h1>Privacy Policy</h1>
     
      <CookieBotDeclaration />
    </div>
  );
};

export default PrivacyPolicyPage;

You should see this once you visit whatever your URL is /privacy-policy

Conclusion

Cookie Policy Popups are an important part of websites for legal compliance and user transparency/trust.  I hope you have a better understanding of how this works in headless WordPress, focusing on the frontend.

As always, stoked to hear your feedback and any questions you might have on headless WordPress! Hit us up in our Discord!

The post Cookie Policy Popups in headless WordPress with Next.js 14 and WPGraphQL 🍪 appeared first on Builders.

]]>
https://wpengine.com/builders/cookie-policy-popups-in-headless-wp/feed/ 0