DEV Community

lokprakash babu
lokprakash babu

Posted on

On-demand revalidation ⚡️

Table of Contents

 1. Introduction:
 2. What is On-demand validation and how it works:
 3. Setup:
 4. A Catch
 5. Conclusion
 6. References

Introduction:

When a blog page or a product landing page is created, we prefer them to be static pages. Because, it is less likely to have a dynamic content or complex interactions on them. In an organisation, it is not scalable to have a developer involvement for publishing each marketing page or blog page. Hence, it is logical to off board the publishing process from developer to the respective teams which are managing the content.

This leads to separation of content rendering and content publishing process. The ideal flow would be, when a team publishes a content(in a certain tool), the content should reflect in the desired target(website maybe) without any developer involvement.

In this blog, we will discuss on how to achieve the above mentioned functionality using a headless CMS, Next JS and Vercel's infrastructure.

What is On-demand validation and how it works:

When a static website is deployed to Vercel, the page is actually served from Vercel's CDN, it is also called as 'edge network'. Edge network lies near to a consumer region to serve a swiftly. When a deployment happens the CDN's cache will be invalidated. Hence, when a request comes to a CDN, the page request will be served from Vercel's server and the new page will be stored in CDN's Cache.

There can be an use-case where a page might have to refresh its content in a periodic time interval. To support this Incremental static regeneration is available.

However, there can also be certain use cases, where a page doesn't have to refresh its content in an interval, but, it should reflect a new/updated content whenever a new data is added, deleted or updated in a CMS. On-demand validation will help us solve this.

When an operation is performed on a content in a CMS, we will make use of revalidate api from the NEXT JS, to invalidate a cache in the Edge CDN, which in-turns refresh a respective page. Basically, it is imitating a deployment for a particular page.

Setup:

We are going to use Strapi and Next JS(Page router) in this blog post to create a blog listing page.

Create a Next app with page router configuration
npx create-next-app@latest

Create a strapi project yarn create strapi-app my-blogs-strapi

Before proceeding to our Next JS project, let's finish our Strapi part.

In Strapi, we will create a content type which represents how a blog post looks like. Once, a content type is defined, an author can publish a content corresponding to a content type using content manager.

Content Type and Content Manager

For the scope of this blog post, let's create a content type called blog which has blogTitle and blogDescription fields.
Content type - blog

After creating a content type, we will create Webhook for CRUD(Create, Read, Update and Delete) operations.

Webhook creation

To create a Webhook, an endpoint for making a post request by Strapi when there are any operation performed on a content type, needs to be created. We will use Next JS api route to create the endpoint.

Webhook creation

Let's create a file named revalidate.js inside the api folder. The path for revalidation will be /api/revalidate. A POST request with a request body will be made to this endpoint by Strapi.

The request body associated from strapi has three main attributes,

  • entry
  • model
  • event

event: Describes whether a content is created, updated, published or unpublished.

model: Represents a content type. In this case, it is blog.

entry: Represents the entry on which an action is performed from content manager.

Sample payloads can be referred from the codeblocks.

{
  event: 'entry.create',
  createdAt: '2023-06-11T11:42:51.225Z',
  model: 'blog',
  uid: 'api::blog.blog',
  entry: {
    id: 1,
    blogTitle: 'Blog 1',
    blogDescription: 'Blog description 1',
    createdAt: '2023-06-11T11:42:51.220Z',
    updatedAt: '2023-06-11T11:42:51.220Z',
    publishedAt: null
  }
}
Enter fullscreen mode Exit fullscreen mode
{
  event: 'entry.update',
  createdAt: '2023-06-11T11:42:54.458Z',
  model: 'blog',
  uid: 'api::blog.blog',
  entry: {
    id: 1,
    blogTitle: 'Blog 1',
    blogDescription: 'Blog description 1',
    createdAt: '2023-06-11T11:42:51.220Z',
    updatedAt: '2023-06-11T11:42:54.456Z',
    publishedAt: '2023-06-11T11:42:54.455Z'
  }
}
Enter fullscreen mode Exit fullscreen mode
{
  event: 'entry.publish',
  createdAt: '2023-06-11T11:42:54.458Z',
  model: 'blog',
  entry: {
    id: 1,
    blogTitle: 'Blog 1',
    blogDescription: 'Blog description 1',
    createdAt: '2023-06-11T11:42:51.220Z',
    updatedAt: '2023-06-11T11:42:54.456Z',
    publishedAt: '2023-06-11T11:42:54.455Z'
  }
}
Enter fullscreen mode Exit fullscreen mode

However, we are interested in the attributes event and model. Because, event informs us what happened to a content type, and model describes what content-type got updated. Although, these attributes solves for the scope of this blog, if we have to revalidate a specific page, like blog details page, entry attribute will be useful. Without model, it might not be possible to know which page to be revalidated. For example, if a model is blog and a blog listing page is available, then, whenever an action is performed on the blog content type we have to revalidate the blog listing page.

Sample code for revalidate.js

const modelToPageMap = {
  blog: "/blogs",
};

export default async function handler(req, res) {
  const requestMethod = req.method;
  if (requestMethod === "POST") {
    const requestBody = req.body;
    const model = requestBody.model;
    await res.revalidate(modelToPageMap[model]);
    return res.json({ revalidated: true });
  } else {
    return res.status(405).json({
      text: "Method not supported",
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

A Catch

We are passing a path to revalidate api. There are chances where a path doesn't exist. For example, when we create a new blog, the api will revalidate with /blog/[new-blog-url]. However, this path doesn't exist. Hence, revalidation will fail. In order to handle this scenario, a good practise is to pair getStaticPaths and getStaticProps. Instead of returning 404 for not found paths, if a fallback true is returned, a loader screen can be shown while the getStaticProps fetches a particular page's data. If the data is not available, we can redirect to 404 or handle in a customisable manner. Once the new page is constructed, revalidation can be performed when an update or un-publish action occur.

Conclusion

On-demand revalidation helps us optimizing the number of revalidation performed when a page is Incremental Static Regenerated. Most of the matured CMS will have webhook to implement this functionality. For the purpose of this blog, the endpoint for webhook is given in localhost. For production-ready it needs to be replaced with an actual https endpoint.

References

Top comments (0)