Ketika aplikasi Single Page Application (SPA) berbasis Inertia.js di-deploy ke produksi, browser klien yang masih membuka tab lama akan memegang referensi ke asset chunk yang berpotensi sudah dihapus dari server. Upaya navigasi berikutnya akan menghasilkan ChunkLoadError dan merusak alur pengguna. Inertia menyelesaikan masalah ini lewat mekanisme asset versioning otomatis berbasis header HTTP.
Mekanisme Protokol Asset Versioning Inertia
Inertia mendeteksi perbedaan versi antara klien dan server dengan mengevaluasi header pada setiap request internal:
- Pengiriman Versi: Klien menyertakan header
X-Inertia-Versionyang berisi hash aset frontend saat aplikasi pertama kali dimuat. - Validasi Middleware: Middleware
HandleInertiaRequestsdi backend membandingkan nilai header tersebut dengan hash manifest saat ini (misalnya hasil dariVite::manifestHash()). - Respons 409 Conflict: Jika hash tidak identik pada request
GET, server membatalkan render komponen dan mengembalikan status HTTP409 Conflictdisertai headerX-Inertia-Location: [target_url]. - Hard Reload: Klien Inertia menangkap status 409 tersebut, lalu memaksa browser melakukan hard navigation (
window.location.href) ke URL target guna mengunduh HTML dan bundle JavaScript terbaru.
Integrasi Test Backend: Memverifikasi 409 Conflict
Pengujian backend memastikan middleware bereaksi tepat terhadap versi usang dan tidak meloloskan payload JSON usang ke klien. Implementasi menggunakan Pest PHP di Laravel:
<?php
use Inertia\Inertia;
it('mengembalikan 409 conflict dan header X-Inertia-Location saat versi aset mismatch', function () {
// ponytail: Mock versi statis untuk isolasi tes unit middleware
Inertia::version('manifest-hash-v2');
$response = $this->withHeaders([
'X-Inertia' => 'true',
'X-Inertia-Version' => 'manifest-hash-v1-outdated',
])->get('/dashboard');
$response->assertStatus(409);
$response->assertHeader('X-Inertia-Location', url('/dashboard'));
});
it('mengembalikan 200 ok saat versi aset sinkron', function () {
Inertia::version('manifest-hash-v2');
$response = $this->withHeaders([
'X-Inertia' => 'true',
'X-Inertia-Version' => 'manifest-hash-v2',
])->get('/dashboard');
$response->assertOk();
$response->assertHeaderMissing('X-Inertia-Location');
});
Pengujian di atas membuktikan bahwa request berstatus usang langsung dipotong sebelum masuk ke rendering controller.
Mitigasi Request Non-GET saat Version Mismatch Terjadi
Protokol default Inertia memperlakukan request non-GET (seperti POST, PUT, DELETE) secara berbeda saat version mismatch terjadi:
Inertia tidak langsung mengembalikan respons 409 Conflict pada request non-GET untuk mencegah eksekusi ulang aksi mutasi data secara tidak sengaja (non-idempotent action). Klien diarahkan untuk mengeksekusi request ulang via GET ke URL sebelumnya.
Untuk menghindari hilangnya data input form ketika mismatch terjadi di tengah submit transaksi:
- State Preservation: Gunakan form helper bawaan Inertia (
useForm) dengan opsiremember: trueagar form state tersimpan di browser storage. - Client-Side Interception: Tangkap event global
router.on('invalid', callback)untuk mendeteksi mismatch sebelum request mutasi dijalankan jika hash aplikasi diketahui sudah kedaluwarsa.
Verifikasi E2E Menggunakan Playwright
Uji integrasi browser memastikan klien Inertia merespons 409 dengan melakukan reload fisik tepat satu kali dan tidak terjebak dalam infinite reload loop akibat kegagalan sinkronisasi hash.
import { test, expect } from '@playwright/test';
test('klien mengeksekusi hard reload saat menerima 409 conflict', async ({ page }) => {
let reloadCount = 0;
page.on('framenavigated', (frame) => {
if (frame === page.mainFrame()) {
reloadCount++;
}
});
await page.goto('/dashboard');
expect(reloadCount).toBe(1);
// Simulasikan endpoint backend merespons 409 saat navigasi client-side
await page.route('**/settings', async (route) => {
await route.fulfill({
status: 409,
headers: {
'X-Inertia-Location': 'http://localhost/settings',
},
});
});
// Trigger navigasi Inertia
await page.click('a[href="/settings"]');
// Pastikan browser menjalankan navigasi penuh ke URL tujuan
await page.waitForURL('**/settings');
// Reload total harus tepat 2 (navigasi awal + 1 hard reload)
expect(reloadCount).toBe(2);
});
Otomasi Validasi Hash di Pipeline CI
Di lingkungan CI/CD, verifikasi bahwa backend dapat membaca hash hasil build Vite tanpa kegagalan parsing manifest. Tambahkan langkah verifikasi eksplisit pada GitHub Actions:
name: CI Test Suite
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Build Frontend Assets
run: |
npm ci
npm run build
- name: Validate Manifest Generation
run: |
test -f public/build/manifest.json || { echo "Vite manifest missing"; exit 1; }
- name: Run Pest Tests (with Real Manifest)
run: |
php artisan test --filter=InertiaVersionTest
Pipeline ini mencegah rilis kode backend yang tidak sinkron dengan lokasi output direktori build frontend, memastikan proteksi asset mismatch selalu aktif di level runtime produksi.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!