Optimizing Next.js Data Refresh: A Guide to Revalidation with Medusa.js

By Viktor Holik

Featured image

Welcome to our guide on setting up revalidation in a Next.js application using an internal server. In this tutorial, we’ll explore different revalidation types and how to enhance their security.

For this demonstration, we’ll be using the Medusa.js backend server, though feel free to use any backend server of your choice.

Why Revalidation is Essential?

Revalidation is crucial to avoid fetching data from your source on every request, boosting your web application’s performance. By default, Next.js caches requests using fetch, including POST requests. Without revalidation or opting out of data cache, your application would essentially become static.

Two Types of Revalidation:

Time-based revalidation

Time-based revalidation is a straightforward approach to setting cache lifetimes for data in your Next.js application. By specifying an interval (in milliseconds), you define how long the data should be considered valid in the cache. Here’s how you can implement it:

fetch('https://example.pl/api/products', { next: { revalidate: 3600 } })

In this example, the revalidate option is set to 3600 seconds (1 hour). This means that the data fetched from the given API endpoint will be considered fresh for up to 1 hour. After this period, a new request will be made to refresh the data.

Alternatively, you can use the revalidate property in the page or component file itself:

// app/products/page.tsx

export const revalidate = 3600 // revalidate at most every hour

This approach is particularly useful when you want to update your cache periodically, ensuring that your application is serving relatively recent data without overwhelming your server with constant requests.

On-demand revalidation

On-demand revalidation provides the flexibility to refresh data only when needed, and you can achieve this using cache tags or specific paths inside a server action or route handler. Here’s how you can set it up:

export default async function Page() {
  const res = await fetch(
    'https://example.pl/api/products', 
    { next: { tags: ['products'] } } // Here is where the magic happens
  ) 
  const data = await res.json()

  return (
  ...
}

Let’s make a route handler in Next.js to test this

// app/api/revalidate/[tag]/route.ts

export async function POST(
  request: NextRequest,
  { params }: { params: { tag: string } }
) {
  const tag = params.tag

  revalidateTag(tag);

  return NextResponse.json({ revalidated: tag });
}

When we access this route using the URL http://localhost:3000/api/revalidate/products we will purge cache and refetch with the newest data.

Implementing Revalidation with an Internal Server

Let’s enhance our route handler by including a secret key in the query parameters. This ensures that only authorized users can trigger the revalidation process manually.

// app/api/revalidate/[tag]/route.ts

export async function POST(
  request: NextRequest,
  { params }: { params: { tag: string } }
) {
  const tag = params.tag
  // Retrieve secret key from search params
  const secret = request.nextUrl.searchParams.get('secret');

  // Verify secret key
  if (secret !== process.env.REVALIDATE_SECRET) {
      return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  revalidateTag(tag);

  return NextResponse.json({ revalidated: tag });
}

Also add REVALIDATE_SECRET in your environment variables:

// .env
REVALIDATE_SECRET=supersecret_revalidate_key

Now, let’s navigate to our server (assuming we’re using Medusa.js) and implement a subscriber for every product update.

// src/subscribers/product.ts
import axios from "axios";

class ProductSubscriber {
  constructor({ eventBusService }) {
    eventBusService.subscribe(
      "product.created",
      this.revalidateOnDemand
    );
    eventBusService.subscribe(
      "product.updated",
      this.revalidateOnDemand
    );
    eventBusService.subscribe(
      "product.deleted",
      this.revalidateOnDemand
    );
  }

  revalidateOnDemand = async () => {
    await axios.post(process.env.FRONTEND_REVALIDATE_URL/products, {
      params: {
        secret: process.env.FRONTEND_REVALIDATE_SECRET
      },
    });
  };
}

export default ProductSubscriber;

Wrapping up

By leveraging these strategies, you can strike a balance between performance optimization and data freshness. The addition of a secret key in the URL parameters ensures that only authorized users can trigger manual revalidation, enhancing the security of your application.

I hope you found this article helpful.

Got questions or need a hand with Next.js revalidation?

Reach out to us!

Other blog posts

Maintance Mode in Next.js Applications

But how to implement maintenance mode in Next.js? Is it as easy as configuring a plugin on WordPress for a few minutes? Of course it is!

Medusa vs Magento: Total cost of ownership

Magento, compared to Medusa, may lead to higher long-term costs due to its licensing model and the risk associated with the gradual decline in the popularity of the PHP language...

Tell us about your project

Got a project in mind? Let's make it happen!

By clicking “Send Message” you grant us, i.e., Rigby, consent for email marketing of our services as part of the communication regarding your project. You may withdraw your consent, for example via hello@rigbyjs.com.
More information
placeholder

Grzegorz Tomaka

Co-CEO & Co-founder

LinkedIn icon
placeholder

Jakub Zbaski

Co-CEO & Co-founder

LinkedIn icon