Implementasi On-Demand Incremental Static Regeneration (ISR) dengan Next.js

TL;DR

On-Demand Incremental Static Regeneration hanya akan melakukan build jika ada trigger. Sedangkan, Incremental Static Regeneration biasa akan terus menerus melakukan build sesuai revalidate yang telah ditentukan.

Next.js adalah open source React framework yang sangat populer. Next.js mempunyai fitur unggulan seperti strategi data fetching yang dapat kita sesuaikan dengan kebutuhan aplikasi yang kita buat. Ada Static Site Generation (SSG), Server Side Rendering (SSR), Incremental static regeneration (ISR), dan Client Side Rendering (CSR).

Wah, banyak banget ya. Apaan tuh pengertian dari setiap strategi data fetching tersebut? Biar lebih jelas, coba tonton video dibawah ini!

Sudah lumayan ngerti kan perbedaan dari setiap strategi data fetching tersebut? Kalau sudah kita lanjut ke pembahasan On-Demand ISR.

On-Demand Incremental Static Regeneration (ISR)

Memang apa sih perbedaan nya On-Demand ISR dengan ISR biasa? Perbedaan nya ada adalah kapan page yang sudah digenerate secara static saat build time akan revalidate . Dengan ISR biasa kita perlu menambahkan props revalidate untuk menentukan kapan page akan re-generate. Lebih lengkap nya bisa lihat potongan kode dibawah ini.

export async function getStaticProps() { 
	const res = await fetch('https://.../posts') 
	const posts = await res.json() 
	return { 
		props: {
			posts
		}, 
		// Next.js will attempt to re-generate the page: 
		// - When a request comes in 
		// - At most once every 10 seconds 
		revalidate: 10, // In seconds 
}}

revalidate: 10 menunjukan bahwa page akan re-generate per 10 detik. Sedangkan, On Demand ISR tidak memerlukan props revalidate. Terus gimana caranya page tersebut dapat di-update? Sesuai namanya "On-Demand" page akan di-update jika ada trigger / request yang meminta halaman tersebut untuk revalidate. Yuk kita bahas cara implementasinya!

Implementasi On-Demand ISR

Implementasi On-Demand ISR akan kita coba gunakan dengan webhook strapi sebagai trigger untuk revalidate halaman di aplikasi Next.js kita. Berikut ini langkah-langkahnya

Setup Webhook di Strapi

Klik menu settings lalu pilih webhooks di bagian GLOBAL SETTINGS. Gambar dibawah ini menunjukan halaman untuk mengelola Webhooks di Strapi. Saya sudah punya satu karena sebelumnya telah punya.
Pasted image 20231116094441.png

Klik create new Webhooks
Pasted image 20231116095034.png

Lalu akan muncul halaman seperti ini
Pasted image 20231116095104.png

Berikut ini merupakan penjelasan dari field form diatas

Field Deskripsi Contoh isi
Name Nama webhook yang kita buat Revalidate
URL URL aplikasi Next.js untuk handle webhook http://localhost:3000/api/revalidate
Nanti kita bikin file revalidate ini di Next.js
Headers Header yang dikirim ke aplikasi Next.js key = x-webhook-secret
value = buat pakai command openssl rand -base64 32 lalu copy hasilnya
header ini dipakai buat tambahan security. Biar orang lain ngga bisa akses.
Event Tentuin kapan Webhook yang kita buat akan trigger revalidate Next.js Ceklis semua

Contoh yang sudah diisi
Pasted image 20231116102342.png

Handle Webhook di Next.js

Selanjutnya kita buat file revalidate.ts di folder api atau sesuaikan dengan url yang sudah ditargetkan pada setting webhooks di Strapi.

Pasted image 20231116104059.png

Lalu buat file .env yang isinya secret token.

Buat type untuk response sama request. Kalau ngga pakai typescript ngga usah buat ya. Contoh isi payload strapi bisa liat di halaman Payload Webhooks.

type handlerProps = {
  req: {
    headers: {
      'x-webhook-secret': string
    }
    body: {
      model: string
      entry: {
        id: string
      }
    }
  }
  res: {
    revalidate: (path: string) => Promise<void>
    status: (code: number) => {
      json: (data: { message: string; model?: string }) => void
      send: (data: string) => void
    }
  }
}

Buat fungsi handler nya

export default async function handler({ req, res }: handlerProps) {

  // Check for secret to confirm this is a valid request
  const secret = req.headers['x-webhook-secret']
  if (secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  try {
    const { model } = req?.body
    // this should be the actual path not a rewritten path
    // e.g. for "/blog/[slug]" this should be "/blog/post-1"
    switch (model) {
      case 'homepage':
        await res.revalidate('/')
        break
      case 'blog':
        await res.revalidate('/blog')
        break
      default:
        console.log('default')
        break
    }
    console.log('revalidate success')

    return res.status(200).json({ message: 'Revalidated', model: model || '' })

  } catch (err) {
    // If there was an error, Next.js will continue
    // to show the last successfully generated page
    return res.status(500).send('Error revalidating')
  }
}

Ok ini penjelasannya

Di bagian ini, kita cek apakah secret yang ada di header itu cocok dengan secret yang di .env

  // Check for secret to confirm this is a valid request
  const secret = req.headers['x-webhook-secret']
  if (secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: 'Invalid token' })
  }

Jika cocok, kita mapping apa nama model yang membuat event sehingga webhooks melakukan trigger dan revalidate halaman yang sekiranya perlu di-update jika model tersebut ada perubahan. Misalnya, kita update blog dan halaman /blog perlu revalidate ulang untuk menampilkan data blog terbaru.

try {
    const { model } = req?.body
    // this should be the actual path not a rewritten path
    // e.g. for "/blog/[slug]" this should be "/blog/post-1"
    switch (model) {
      case 'homepage':
        await res.revalidate('/')
        break
      case 'blog':
        await res.revalidate('/blog')
        break
      default:
        console.log('default')
        break
    }
    console.log('revalidate success')

    return res.status(200).json({ message: 'Revalidated', model: model || '' })

  } catch (err) {
    // If there was an error, Next.js will continue
    // to show the last successfully generated page
    return res.status(500).send('Error revalidating')
  }
}

kode keseluruhan

type handlerProps = {
  req: {
    headers: {
      'x-webhook-secret': string
    }
    body: {
      model: string
      entry: {
        id: string
      }
    }
  }

  res: {
    revalidate: (path: string) => Promise<void>
    status: (code: number) => {
      json: (data: { message: string; model?: string }) => void
      send: (data: string) => void
    }
  }
}
  
export default async function handler({ req, res }: handlerProps) {

  // Check for secret to confirm this is a valid request
  const secret = req.headers['x-webhook-secret']
  if (secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: 'Invalid token' })
  }

  try {
    const { model } = req?.body
    // this should be the actual path not a rewritten path
    // e.g. for "/blog/[slug]" this should be "/blog/post-1"
    switch (model) {
      case 'homepage':
        await res.revalidate('/')
        break
      case 'blog':
        await res.revalidate('/blog')
        break
      default:
        console.log('default')
        break
    }
    console.log('revalidate success')
    return res.status(200).json({ message: 'Revalidated', model: model || '' })
  } catch (err) {
    // If there was an error, Next.js will continue
    // to show the last successfully generated page
    return res.status(500).send('Error revalidating')
  }
}

Untuk mencoba nya secara langsung, jalankan command npm run build lalu npm run start. Setelah itu lakukan perubahan data di Strapi. Jika berhasil maka console akan memunculkan revalidate success dan halaman yang ditargetkan akan memunculkan konten terbaru setelah di-refresh.

atau coba Live demo On-Demand ISR Vercel

Selamat Mencoba!