Memahami masalah timeout ISR pada API route Next.js

Ketika API route Next.js dengan Incremental Static Regeneration (ISR) mulai timeout saat melakukan fetch data eksternal, akar masalahnya bukan hanya pada provider data tapi juga bagaimana cache dan timeout dikonfigurasi. Artikel ini langsung membedah gejala observability, cara reproduksi, root cause investigation, serta solusi teknis yang membetulkan timeout dan melindungi cache ISR.

Gejala observability dan langkah reproduksi

Observability

Tim mencatat tiga tanda yang muncul bersamaan: (1) log API route memperlihatkan request stuck lebih dari 10 detik tanpa response, (2) metric latency pada Vercel/Cloud Provider naik drastis saat revalidate berjalan, dan (3) browser menerima response 504 Gateway Timeout pada ISR fallback yang memicu revalidate.

Reproduksi

Untuk mereproduksi, cukup invoke API route yang melakukan fetch ke service eksternal dan tunggu hingga cache mencapai waktu revalidate (misal 30 detik). Jika service luar lambat atau tidak merespons, revalidate akan memblokir request berikutnya hingga timeout, yang menciptakan cascade timeout saat cache kosong.

Penelusuran root cause

Batas waktu fetch dan retry blocking

Investigasi pertama kali melihat bahwa fetch menggunakan konfigurasi fetch(url) tanpa timeout eksplisit. Tanpa timeout, request menunggu hingga default runtime (kadang 60 detik) sebelum gagal, sehingga ISR tidak bisa menyelesaikan revalidate dalam window cache dan hanya menunggu. Log juga menunjukkan bahwa revalidate baru dimulai setelah request pertama selesai, menyebabkan blocking.

Menambahkan instrumentasi seperti console.time atau middleware observability YAML membantu mengidentifikasi tahap fetch yang lama. Selain itu, metrics retry menunjukkan API route mencoba ulang fetch berulang kali, yang memperpanjang waktu keseluruhan.

Kemungkinan konfigurasi cache ISR

Cache ISR di Next.js diarahkan oleh parameter revalidate dan behavior fallback. Jika revalidate terlalu kecil dan data eksternal butuh waktu lebih lama dari nilai itu, timer akan memicu revalidate sebelum cache sebelumnya selesai membentuk response. Apalagi jika fallback diatur ke blocking, klien menunggu server menyelesaikan fetch.

Perbaikan nyata dan mitigasi

Adjust timeout dan fallback cache

Solusi pertama adalah menetapkan timeout eksplisit pada fetch menggunakan AbortController. Timeout 5–10 detik cukup untuk menghindari blocking, lalu menggunakan versi cached response saat timeout terjadi:

const controller = new AbortController();const timeout = setTimeout(() => controller.abort(), 8000);try {const res = await fetch(externalUrl, { signal: controller.signal });} finally {clearTimeout(timeout);}

Jika fetch gagal, response sebelumnya tetap tersedia karena ISR menggunakan cache, sehingga klien tidak langsung mendapatkan error 504.

Retry terkontrol dan circuit breaker

Implementasi retry perlu dibatasi agar tidak menumpuk. Alternatifnya, gunakan circuit breaker sederhana: jika fetch gagal dua kali berturut-turut, skip revalidate dan biarkan cache yang terakhir tetap hidup. Penambahan flag status atau metric error membantu tim memutuskan kapan circuit breaker reset.

Cache fallback dan persepeda ulang request

Ketika cache belum siap, pakai fallback blocking hanya jika fetch sangat cepat. Jika tidak, fallback stable atau manual cache/persepeda ulang request (misalnya using Redis key TTL) bisa memberikan fallback response sementara. Periksa header x-nextjs-cache dan x-vercel-cache untuk memastikan cache hit atau miss.

Verifikasi regresi dan lesson learned

Testing regresi

Setelah perubahan, lakukan:

  • Load test ringan pada API route dengan simulasi timeout provider.
  • Observability check memastikan log/error metric drop.
  • Smoke test memastikan cache lama tetap bisa diakses saat revalidate gagal.

Pengujian ini memastikan ISR tidak lagi memblokir klien saat fetch eksternal gagal.

Lesson learned tim

Beberapa pelajaran penting: (1) Timeout eksplisit wajib untuk endpoint yang tergantung data eksternal agar ISR tidak tergantung pada default runtime. (2) Observability dan metric pada cache ISR memberikan sinyal lebih cepat ketimbang error 504. (3) Circuit breaker membuat sistem tetap responsif saat provider bermasalah.

Dengan analisis ini, tim dapat memastikan API route Next.js dengan ISR tetap stabil meskipun provider luar lambat atau offline.