Dilema Arsitektur: Dynamic SSR vs Hybrid Rendering

Server-Side Rendering (SSR) dinamis murni mengeksekusi siklus hidup Vue dan permintaan data backend pada setiap inbound request. Pola ini menjamin data selalu segar, namun menimbulkan biaya komputasi V8 yang linier terhadap volume trafik. Ketika trafik melonjak, bottleneck berpindah ke thread JavaScript runtime dan antrean koneksi database.

Nuxt 3 mengatasi batas skalabilitas ini melalui Hybrid Rendering yang didukung oleh engine Nitro. Melalui konfigurasi routeRules, developer dapat menentukan strategi rendering per rute secara granular: On-Demand Dynamic SSR, Server-side Revalidation (SWR), atau Incremental Static Regeneration (ISR). Namun, beralih ke ISR bukan solusi instan tanpa konsekuensi teknis. Pendekatan ini memperkenalkan latensi revalidasi, kompleksitas invalidasi storage, dan risiko kebocoran data pengguna.

Mekanisme Eksekusi: Siklus Hidup Request

1. Dynamic SSR (On-Demand)

Pada dynamic SSR murni, alur eksekusi terjadi secara serial:

  1. Klien mengirimkan request HTTP.
  2. Nitro menginisialisasi context, mengeksekusi middleware, dan memanggil useAsyncData atau API backend terkait.
  3. Runtime me-render virtual DOM menjadi HTML string melalui vue/server-renderer.
  4. Payload HTML dan serialisasi state (pinia atau useState) dikirimkan ke klien.

Kelemahan utama: Lonjakan beban menghasilkan peningkatan Time to First Byte (TTFB) secara eksponensial saat CPU terbebani tugas rendering DOM dan eksekusi event-loop terblokir oleh serialisasi data besar.

2. ISR dan SWR (Nitro Engine Cache)

SWR dan ISR menyimpan hasil render HTML ke dalam layer unstorage bawaan Nitro. Perbedaannya terletak pada perilaku CDN dan storage adapter:

  • SWR (Stale-While-Revalidate): Nitro langsung menyajikan respons dari cache (stale) jika tersedia, lalu menjalankan background task untuk me-render ulang halaman dan memperbarui cache secara asinkron.
  • ISR (Incremental Static Regeneration): Menyelaraskan cache lokal Nitro dengan CDN edge cache menggunakan header Cache-Control tertentu, mempertahankan halaman statis hingga periode TTL (Time-to-Live) kedaluwarsa sebelum revalidasi diinisiasi.

Analisis Trade-off Teknis

1. TTFB dan Beban Query Database

Pada Dynamic SSR, database relasional atau microservice hulu menerima thundering herd problem saat ribuan klien mengakses rute yang sama secara simultan. Pada ISR/SWR, query hulu dipadatkan menjadi satu pemanggilan periodik sesuai interval revalidasi. TTFB terpangkas dari ratusan milidetik (atau hitungan detik saat komputasi tinggi) menjadi single-digit milidetik karena respons disajikan langsung dari memory/storage.

2. Eventual Consistency dan Cache Invalidation

Kelemahan fundamental ISR adalah integritas konsistensi data (eventual consistency). Jika stok produk berubah di database, halaman ISR tetap menampilkan data lama hingga siklus revalidasi selesai atau cache dibersihkan secara eksplisit.

Invalidasi programmatic di Nuxt 3 memerlukan interaksi langsung dengan layer storage Nitro. Nitro menyimpan cache rute terkomputasi di namespace cache:nitro:routes.

// server/api/purge-cache.post.ts
import { defineEventHandler, readBody, createError } from 'h3'

export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  const token = getHeader(event, 'x-invalidation-secret')

  if (token !== process.env.PURGE_SECRET_TOKEN) {
    throw createError({ statusCode: 401, statusMessage: 'Unauthorized' })
  }

  // Akses storage engine Nitro
  const storage = useStorage('cache:nitro:routes')
  
  // Format key internal Nitro: _[route_path].json / .html
  const cacheKey = `_${body.path}.json`

  if (await storage.hasItem(cacheKey)) {
    await storage.removeItem(cacheKey)
    return { purged: true, path: body.path }
  }

  return { purged: false, message: 'Cache key not found' }
})
Perhatian: Invalidasi manual berbasis event memicu peningkatan kompleksitas operasional pada sistem terdistribusi bila Nitro instance berjalan pada banyak container terpisah tanpa storage bersama (shared storage).

3. Maintainability: Pemisahan Rute Publik dan State Terotentikasi

Pola anti-pattern paling berbahaya pada Nuxt 3 adalah mengaktifkan ISR/SWR pada rute yang membaca identitas pengguna (seperti auth cookie atau token JWT di header).

Jika halaman yang di-render membaca state otentikasi di level server, HTML yang mengandung data spesifik akun pengguna pertama akan tersimpan di Nitro storage dan dibagikan secara terbuka kepada pengunjung berikutnya (cache poisoning/credential leaking).

Aturan arsitektur:

  • Gunakan ISR/SWR secara eksklusif untuk rute publik/anonim (e.g., katalog produk, dokumentasi, landing page).
  • Untuk rute terotentikasi, gunakan dynamic SSR atau alihkan ke Client-Side Rendering (CSR) dengan ssr: false pada routeRules.
  • Jika rute publik membutuhkan komponen terotentikasi (seperti badge keranjang belanja di navbar), render navbar sebagai <ClientOnly> atau load data melalui API terpisah dari sisi klien.

Analisis Biaya Operasional: Komputasi vs Penyimpanan

KomponenDynamic SSR MurniHybrid Rendering (ISR/SWR)
Beban CPU ServerTinggi (re-render DOM dan parsing data per request).Rendah (hanya terbebani saat interval revalidasi).
Kebutuhan RAMTinggi (footprint memori V8 bertambah seiring konkurensi).Dapat diprediksi, proporsional terhadap ukuran footprint cache HTML.
Infrastruktur CacheTidak memerlukan distributed storage runtime.Membutuhkan backend unstorage (Redis/Valkey) atau filesystem SSD.
Beban DatabaseLinier terhadap request masuk (N request = N queries).Dibatasi oleh interval TTL (N request = 1 query per periode).

Contoh Konfigurasi nuxt.config.ts Skala Produksi

Konfigurasi di bawah mendemonstrasikan pemetaan strategi rendering berbasis rute dan konfigurasi Redis sebagai backend storage Nitro terpusat untuk traffic tinggi multi-instance.

// nuxt.config.ts
export default defineNuxtConfig({
  compatibilityDate: '2024-04-03',

  nitro: {
    // Shared storage untuk cache horizontal scaling
    storage: {
      'cache:nitro:routes': {
        driver: 'redis',
        host: process.env.REDIS_HOST || '127.0.0.1',
        port: Number(process.env.REDIS_PORT) || 6379,
        password: process.env.REDIS_PASSWORD,
        db: 0,
        ttl: 60 * 60 * 24 // Default fallback 24 jam
      }
    }
  },

  routeRules: {
    // 1. Landing page & konten statis: Static Generation
    '/': { prerender: true },
    '/about': { prerender: true },

    // 2. Katalog publik dengan perubahan periodik: ISR 10 Menit
    '/products': { isr: 600 },
    '/products/**': { isr: 600 },

    // 3. Blog/Artikel: SWR dengan stale response instan
    '/blog/**': { swr: 3600 },

    // 4. Halaman dinamis sensitif data real-time: Dynamic SSR
    '/search': { ssr: true },

    // 5. Area terotentikasi: Client-Side Rendering murni (hemat compute server)
    '/admin/**': { ssr: false },
    '/checkout/**': { ssr: false }
  }
})

Matriks Keputusan Arsitektur

Pilih strategi rendering Nuxt 3 berdasarkan karakteristik data dan toleransi latensi rute Anda:

  • Prerender (Static): Data hampir tidak pernah berubah (Docs, Privacy Policy, Terms). Dibangun saat npm run build.
  • ISR (Incremental Static Regeneration): Halaman publik bervolume tinggi, data jarang diperbarui dalam rentang menit (Detail Produk e-commerce, direktori listing). Prioritas: TTFB terendah dan SEO optimal.
  • SWR (Stale-While-Revalidate): Halaman dengan update berkala yang toleran terhadap stale data sesaat (Portal berita, feed blog). Menjamin TTFB instan bagi user kedua dan seterusnya.
  • Dynamic SSR: Halaman publik yang membutuhkan data 100% konsisten detik itu juga (Hasil pencarian dengan filter kompleks, tracking order publik).
  • SPA (Client-Side Rendering via ssr: false): Area dashboard tertutup, rute transaksi, atau form data sensitif. Menghilangkan beban server render sekaligus menghindari risiko kebocoran session storage.