Migrasi Nuxt 4 bukan sekadar menaikkan versi package. Pada aplikasi produksi, proses upgrade Nuxt 3 ke Nuxt 4 harus diperlakukan sebagai perubahan arsitektural kecil yang menyentuh dependency, konfigurasi build, kompatibilitas module, perilaku SSR, pipeline CI/CD, dan strategi rilis. Jika dilakukan tanpa audit dan validasi yang cukup, masalah yang muncul sering bukan pada saat build, melainkan ketika trafik nyata mulai masuk.

Artikel ini adalah panduan Nuxt terbaru yang berfokus pada migrasi bertahap, minim risiko, dan realistis untuk sistem yang sudah berjalan. Tujuannya bukan membuat migrasi terasa mudah secara semu, melainkan membantu Anda memindahkan aplikasi dengan kontrol yang baik atas stabilitas, performa, dan kemampuan rollback.

1. Tentukan strategi migrasi sebelum menyentuh kode

Sebelum menjalankan npm update atau mengubah konfigurasi, tentukan model migrasi yang akan dipakai. Untuk aplikasi produksi, ada tiga pendekatan umum:

  • Big bang migration: seluruh aplikasi dipindahkan sekaligus. Cocok hanya untuk aplikasi kecil dengan trafik rendah dan cakupan module terbatas.
  • Branch-based staged migration: migrasi dikerjakan di branch khusus, diuji penuh, lalu dirilis bertahap. Ini adalah pendekatan paling umum.
  • Parallel environment: Nuxt 3 dan Nuxt 4 berjalan berdampingan di environment berbeda, lalu trafik dialihkan perlahan melalui reverse proxy, load balancer, atau feature flag. Ini paling aman untuk aplikasi dengan SLA ketat.

Jika target Anda adalah tanpa downtime besar, pendekatan kedua dan ketiga biasanya lebih rasional. Kuncinya adalah menghindari perubahan besar yang tidak bisa diisolasi. Pecah migrasi menjadi beberapa checkpoint: update runtime, verifikasi module, refactor struktur folder, validasi rendering, dan final cutover.

Catatan: Jangan menyatukan migrasi Nuxt dengan refactor desain besar, perubahan state management, atau migrasi backend dalam satu rilis. Saat terjadi regresi, Anda akan sulit menemukan akar masalahnya.

2. Audit dependency dan identifikasi area berisiko

2.1 Inventaris package inti dan module pihak ketiga

Langkah pertama dalam migrasi Nuxt 4 adalah memetakan seluruh dependency yang berhubungan langsung dengan framework:

  • nuxt
  • nitro
  • module resmi Nuxt
  • module komunitas seperti auth, i18n, image, sitemap, analytics
  • integrasi bundler, linter, test runner, dan tooling build

Gunakan perintah berikut untuk melihat package usang:

npm outdated
npm ls --depth=0

Lalu buat tabel audit sederhana dengan kolom:

  • Nama package
  • Versi saat ini
  • Versi target
  • Status kompatibilitas Nuxt 4
  • Catatan migrasi
  • Alternatif jika tidak kompatibel

Yang paling berisiko biasanya adalah module yang:

  • Mengakses hook internal Nuxt/Nitro
  • Bergantung pada struktur folder lama
  • Memanipulasi SSR context secara langsung
  • Menggunakan plugin client/server yang tidak eksplisit

2.2 Cek kompatibilitas package kritis

Jangan mengasumsikan semua module Nuxt 3 otomatis aman di Nuxt 4. Periksa:

  • Release note package
  • Dokumentasi migrasi resmi
  • Issue GitHub terkait Nuxt 4
  • Peer dependency yang diminta

Jika ada module yang belum mendukung Nuxt 4, pilih salah satu:

  1. Tunda upgrade sampai module siap
  2. Ganti dengan package lain yang kompatibel
  3. Implementasikan kebutuhan tersebut secara manual jika ruang lingkupnya kecil

Kesalahan umum adalah memaksa instalasi dengan --force atau --legacy-peer-deps lalu menganggap masalah selesai. Cara ini mungkin meloloskan instalasi, tetapi tidak menjamin perilaku runtime aman.

3. Siapkan branch migrasi dan baseline pengujian

Sebelum mengubah versi framework, bekukan baseline aplikasi saat ini. Anda perlu tahu apakah regresi nanti benar-benar akibat Nuxt 4, atau memang sudah ada sebelumnya.

3.1 Buat baseline yang bisa dibandingkan

  • Simpan hasil build produksi Nuxt 3
  • Catat metrik utama: error rate, TTFB kasar, rute penting, halaman dengan SSR, dan API internal
  • Ambil snapshot HTML untuk halaman kritis
  • Pastikan test saat ini benar-benar hijau

3.2 Prioritaskan area yang harus lolos uji

Minimal, definisikan smoke test untuk:

  • Homepage
  • Halaman detail berbasis dynamic route
  • Halaman login atau protected route
  • Halaman dengan data SSR
  • Form penting
  • Navigasi antar halaman
  • Error page dan fallback route

Jika Anda belum punya automated test lengkap, siapkan setidaknya skrip end-to-end dasar sebelum migrasi dimulai. Ini jauh lebih murah dibanding debugging bug produksi setelah rilis.

4. Upgrade dependency inti dan update konfigurasi

4.1 Lakukan upgrade secara eksplisit

Naikkan package inti secara terkontrol, bukan sekaligus semua tooling non-esensial. Contoh:

npm install nuxt@latest
npm install

Setelah itu, jalankan:

npm run dev
npm run build
npm run preview

Tujuannya adalah mendeteksi error pada tiga fase berbeda:

  • dev: masalah HMR, plugin, alias, dan import
  • build: masalah bundling, static analysis, dan server bundle
  • preview: masalah runtime yang baru muncul setelah artefak produksi dibuat

4.2 Tinjau nuxt.config

Setelah upgrade Nuxt 3 ke Nuxt 4, periksa konfigurasi yang mungkin perlu dirapikan, terutama bagian berikut:

  • modules
  • runtimeConfig
  • app
  • routeRules
  • nitro
  • vite
  • typescript

Contoh konfigurasi yang lebih eksplisit dan mudah diaudit:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxtjs/i18n'
  ],
  runtimeConfig: {
    apiSecret: process.env.API_SECRET,
    public: {
      apiBase: process.env.NUXT_PUBLIC_API_BASE || '/api'
    }
  },
  routeRules: {
    '/': { prerender: true },
    '/dashboard/**': { ssr: true },
    '/api/**': { cors: true }
  },
  nitro: {
    compressPublicAssets: true
  },
  typescript: {
    strict: true
  }
})

Hindari membawa konfigurasi lama yang sebenarnya sudah tidak digunakan. Konfigurasi yang usang sering memperumit debugging karena terlihat valid tetapi tidak lagi berpengaruh.

4.3 Validasi environment variable

Perubahan kecil pada cara aplikasi membaca environment variable bisa berdampak besar pada produksi. Pastikan:

  • Rahasia server hanya diakses dari server
  • Nilai publik masuk ke runtimeConfig.public
  • Build environment dan runtime environment konsisten di CI/CD serta container

Masalah umum: aplikasi berjalan baik di lokal tetapi gagal di server karena variabel tersedia saat build, namun tidak tersedia saat runtime.

5. Refactor struktur folder dan pola kode yang sensitif

5.1 Audit folder aplikasi

Nuxt modern makin menekankan konvensi yang jelas. Saat migrasi, cek folder seperti:

  • pages/
  • components/
  • composables/
  • plugins/
  • middleware/
  • server/api/
  • server/middleware/

Yang perlu diperhatikan bukan hanya nama folder, tetapi pola penempatannya. Misalnya, plugin yang semula diam-diam memakai API browser saat SSR harus dipisah menjadi plugin client-only.

5.2 Perjelas plugin client dan server

Contoh masalah umum:

// plugins/chart.ts
import Chart from 'chart.js/auto'

export default defineNuxtPlugin(() => {
  return {
    provide: { Chart }
  }
})

Di SSR, package seperti ini bisa gagal karena mengakses window atau DOM. Solusinya adalah menjadikannya plugin khusus client:

// plugins/chart.client.ts
import Chart from 'chart.js/auto'

export default defineNuxtPlugin(() => {
  return {
    provide: { Chart }
  }
})

Jika tidak dipisah, gejalanya bisa berupa error 500 saat SSR, hydration mismatch, atau halaman kosong pada rute tertentu.

5.3 Rapikan composable dan pemanggilan data

Periksa penggunaan useAsyncData, useFetch, dan composable kustom. Pastikan:

  • Kunci cache stabil
  • Pemanggilan tidak menggandakan request di client dan server tanpa alasan
  • Data sensitif tidak bocor ke payload publik

Masalah umum saat migrasi adalah request API menjadi dua kali karena composable dipanggil dalam kondisi yang berubah antara SSR dan CSR.

6. Validasi SSR, CSR, dan hydration

Banyak bug migrasi muncul bukan karena build gagal, tetapi karena output SSR dan client render tidak konsisten. Ini harus diuji secara sengaja.

6.1 Cek gejala hydration mismatch

Perhatikan warning seperti:

  • Text content does not match server-rendered HTML
  • Hydration completed but contains mismatches

Penyebab umumnya:

  • Penggunaan Date.now(), Math.random(), atau data non-deterministik di template
  • Akses window atau document saat SSR
  • Perbedaan locale/format tanggal antara server dan client
  • Conditional rendering yang bergantung pada state hanya tersedia di browser

Contoh perbaikan:

<script setup lang="ts">
const mounted = ref(false)
onMounted(() => {
  mounted.value = true
})
</script>

<template>
  <ClientOnly>
    <ExpensiveBrowserWidget v-if="mounted" />
  </ClientOnly>
</template>

Trade-off: pendekatan ini aman untuk widget browser-only, tetapi jangan terlalu sering membungkus komponen dengan ClientOnly karena dapat mengurangi manfaat SSR.

6.2 Uji route dengan mode render berbeda

Jika aplikasi Anda memakai kombinasi SSR, prerender, dan route API, uji semuanya. Halaman yang lolos di mode prerender belum tentu aman di SSR dinamis. Begitu juga endpoint server bisa gagal walaupun halaman statis terlihat normal.

7. Testing, observability, dan pipeline CI/CD

7.1 Minimal test yang wajib ada

  • Unit test untuk utility dan composable penting
  • Integration test untuk page yang memanggil API internal
  • E2E test untuk alur pengguna utama
  • Smoke test produksi setelah deploy

Fokuskan test pada area yang paling rentan berubah: auth, middleware, data fetching, navigasi, dan SSR output.

7.2 Update pipeline CI/CD

Pastikan pipeline Anda menjalankan urutan yang jelas:

npm ci
npm run lint
npm run typecheck
npm run test
npm run build

Jika menggunakan container, samakan versi Node.js antara lokal, CI, dan produksi. Perbedaan runtime sering memunculkan bug yang sulit direproduksi, terutama pada build native dependency atau perilaku package tertentu.

7.3 Tambahkan observability saat rollout

Setelah deploy Nuxt 4, pantau setidaknya:

  • HTTP 5xx rate
  • SSR render error
  • Latency route utama
  • Client-side error dari browser monitoring
  • Error API internal di Nitro/server routes

Tanpa observability, strategi rilis bertahap tidak banyak membantu karena Anda tidak punya sinyal kapan harus menghentikan rollout.

8. Strategi rilis bertahap dan rollback plan

8.1 Gunakan deployment yang bisa dibalik cepat

Pilih salah satu pola berikut:

  • Blue-green deployment: dua environment identik, trafik dipindahkan saat siap
  • Canary release: sebagian kecil trafik masuk ke Nuxt 4 lebih dulu
  • Weighted routing: distribusi trafik bertahap lewat load balancer atau gateway

Untuk aplikasi dengan trafik stabil, canary adalah pilihan praktis. Misalnya:

  1. 1% trafik selama 15-30 menit
  2. 10% trafik jika error stabil
  3. 50% trafik setelah validasi manual
  4. 100% trafik jika metrik aman

Angka pastinya tergantung volume trafik dan toleransi risiko, tetapi prinsipnya sama: jangan langsung cutover penuh jika Anda masih bisa mengukur dampak secara bertahap.

8.2 Siapkan rollback plan sebelum deploy

Rollback plan yang baik harus menjawab tiga pertanyaan:

  • Apa yang di-rollback? Image container, package lock, atau seluruh release artifact?
  • Bagaimana cara rollback? Tag release sebelumnya, switch traffic, atau redeploy commit lama?
  • Berapa cepat rollback dapat dilakukan?

Minimal, simpan artefak produksi sebelumnya dan pastikan tim tahu prosedur rollback yang sama. Jangan menunggu insiden untuk mencari tahu bahwa image lama sudah tidak tersedia.

Praktik baik: lakukan simulasi rollback di staging. Banyak tim punya dokumen rollback, tetapi belum pernah mengujinya.

9. Masalah umum saat migrasi dan cara mengatasinya

9.1 Build sukses, tetapi halaman produksi 500

Penyebab umum: environment variable hilang, plugin browser-only dieksekusi di server, atau module pihak ketiga tidak kompatibel penuh pada runtime.

Solusi: cek log server, jalankan npm run preview, audit plugin .client/.server, dan verifikasi runtimeConfig.

9.2 Hydration mismatch pada komponen tertentu

Penyebab umum: data non-deterministik, manipulasi DOM manual, atau state yang hanya muncul setelah mount.

Solusi: pindahkan logika browser ke onMounted, gunakan ClientOnly secara selektif, dan stabilkan output SSR.

9.3 Module lama tidak lagi bekerja

Penyebab umum: package belum menyesuaikan hook internal atau peer dependency.

Solusi: cari versi yang kompatibel, ganti package, atau implementasi manual sementara untuk fungsi yang benar-benar dibutuhkan.

9.4 Request API dobel setelah migrasi

Penyebab umum: pola useFetch atau useAsyncData tidak stabil antara server dan client.

Solusi: audit key cache, hindari pemanggilan bersyarat yang berubah setelah hydration, dan uji halaman dengan network inspector.

10. Checklist akhir sebelum cutover penuh

  1. Semua dependency inti dan module kritis sudah diaudit
  2. nuxt.config dibersihkan dari konfigurasi usang
  3. Plugin client/server sudah dipisah dengan benar
  4. Rute SSR, CSR, prerender, dan API sudah diuji
  5. Smoke test dan E2E test utama hijau
  6. CI/CD menggunakan runtime yang konsisten
  7. Observability aktif di produksi
  8. Rollback plan sudah diuji
  9. Strategi canary atau blue-green siap digunakan

Penutup

Migrasi Nuxt 4 yang aman bukan soal keberanian menaikkan versi, melainkan disiplin dalam audit, validasi, dan rollout. Dengan pendekatan bertahap, Anda bisa upgrade Nuxt 3 ke Nuxt 4 tanpa downtime besar dan tanpa menjadikan produksi sebagai tempat eksperimen.

Gunakan artikel ini sebagai panduan Nuxt terbaru untuk menyusun rencana migrasi yang konkret: mulai dari audit dependency, update config, pengecekan module pihak ketiga, refactor folder, validasi SSR/CSR, testing, CI/CD, sampai rollback plan dan rilis bertahap. Jika semua tahap ini dilakukan dengan rapi, migrasi tidak harus menjadi momen berisiko tinggi, melainkan perubahan terkontrol yang bisa dipertanggungjawabkan secara teknis.