Penggunaan Ziggy pada stack Laravel dan Inertia.js mempermudah pemanggilan rute backend di frontend melalui fungsi pembantu route(). Masalah muncul ketika penamaan rute atau parameternya diubah di Laravel (routes/web.php) tanpa sinkronisasi langsung ke frontend. Kesalahan ketik atau hilangnya parameter wajib baru terdeteksi saat komponen dirender di browser (runtime failure).

Solusinya adalah mengaktifkan type safety secara menyeluruh. Ziggy menyediakan generator definisi tipe TypeScript (.d.ts). Artikel ini membahas cara mengotomasi siklus hidup type generation tersebut pada environment lokal via Vite dan memvalidasinya di pipeline CI/CD.

1. Generate Tipe dan Konfigurasi TypeScript

Ziggy menyediakan flag --types (atau -t) untuk menghasilkan file deklarasi rute TypeScript bersama file konfigurasi JavaScript rute. Jalankan perintah manual berikut untuk inisialisasi awal:

php artisan ziggy:generate resources/js/ziggy.js --types

Perintah tersebut menghasilkan dua file: resources/js/ziggy.js dan resources/js/ziggy.d.ts. Agar TypeScript compiler mengenali signature dari rute Laravel Anda, pastikan path file tersebut terdaftar dalam tsconfig.json:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "paths": {
      "@/*": ["resources/js/*"],
      "ziggy-js": ["./vendor/tightenco/ziggy"]
    }
  },
  "include": [
    "resources/js/**/*",
    "resources/js/ziggy.d.ts"
  ]
}

Pastikan helper route() diimpor atau diinjeksi dengan benar ke dalam aplikasi Anda. Jika menggunakan Vue atau React, deklarasikan file definisi global atau gunakan import langsung dari package:

import { route } from 'ziggy-js';
import { Ziggy } from './ziggy';

// TypeScript kini memvalidasi nama rute dan parameter secara strict
const url = route('users.posts.show', { user: 1, post: 42 });

// Error compile: Type '{ user: number; }' is missing required property 'post'
const brokenUrl = route('users.posts.show', { user: 1 });

2. Otomasi Regenerasi via Custom Vite Plugin

Mengeksekusi command artisan secara manual setiap kali rute PHP berubah merusak Developer Experience (DX). Kita dapat memanfaatkan hooks server Vite untuk memantau perubahan file pada direktori routes/ dan mengeksekusi generasi tipe tanpa merestart Vite dev server.

Tambahkan inline plugin berikut pada vite.config.ts:

import { defineConfig, Plugin } from 'vite';
import laravel from 'laravel-vite-plugin';
import { spawn } from 'node:child_process';

function ziggyWatcher(): Plugin {
  return {
    name: 'vite-plugin-ziggy-watcher',
    configureServer(server) {
      server.watcher.add('routes/**/*.php');
      server.watcher.on('change', (path) => {
        if (path.includes('routes/') && path.endsWith('.php')) {
          const proc = spawn('php', ['artisan', 'ziggy:generate', 'resources/js/ziggy.js', '--types'], {
            shell: true,
            stdio: 'inherit',
          });
          proc.on('close', (code) => {
            if (code === 0) {
              server.ws.send({ type: 'full-reload' });
            }
          });
        }
      });
    },
  };
}

export default defineConfig({
  plugins: [
    laravel({
      input: ['resources/js/app.ts'],
      refresh: true,
    }),
    ziggyWatcher(),
  ],
});

Dengan konfigurasi ini, setiap perubahan pada file di dalam folder routes/ langsung memicu proses artisan di background, memperbarui ziggy.d.ts, dan memicu full-reload pada klien.

3. Parameter Handling & Strict Validation

Ziggy secara otomatis membaca parameter wajib (required parameters) dan opsional dari route definitions. Perhatikan contoh rute berikut:

// routes/web.php
Route::get('/teams/{team}/members/{member?}', [TeamController::class, 'member'])
    ->name('teams.members.show');

File ziggy.d.ts yang digenerate akan menghasilkan tipe berikut untuk rute tersebut:

// Snippet otomatis dari resources/js/ziggy.d.ts
'teams.members.show': [
  { name: 'team', required: true },
  { name: 'member', required: false }
]

Mitigasi Object Binding

Secara default, parameter rute dapat diisi dengan tipe primitif (string | number) atau objek model jika model tersebut memiliki properti yang sesuai. Namun, passing seluruh objek model Eloquent dapat meloloskan properti yang tidak terdefinisi jika tipe model tidak diexport secara akurat.

Gunakan preferensi passing identifier eksplisit atau mapping properti model untuk menjaga ketatnya validasi:

// Hindari passing entire untyped object
route('teams.members.show', team);

// Direkomendasikan: Passing property terdefinisi
route('teams.members.show', { team: team.id, member: member?.id });

4. Verifikasi pada CI/CD Pipeline (GitHub Actions)

Developer terkadang lupa melakukan commit pada file resources/js/ziggy.js dan resources/js/ziggy.d.ts setelah mengubah rute PHP. Untuk mencegah rute usang masuk ke branch utama atau staging, jalankan verifikasi sinkronisasi menggunakan git diff pada pipeline CI.

Berikut contoh job pada .github/workflows/ci.yml:

name: Frontend Verification

on:
  pull_request:
    branches: [main]

jobs:
  check-ziggy-routes:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
          extensions: mbstring, dom, fileinfo

      - name: Install Composer dependencies
        run: composer install --prefer-dist --no-interaction --no-progress

      - name: Generate Application Key
        run: php artisan key:generate --env=testing

      - name: Regenerate Ziggy Types
        run: php artisan ziggy:generate resources/js/ziggy.js --types

      - name: Verify Git Diff
        run: |
          git diff --exit-code resources/js/ziggy.js resources/js/ziggy.d.ts || (
            echo "Error: Ziggy route definitions are out of date. Run 'php artisan ziggy:generate resources/js/ziggy.js --types' and commit the changes."
            exit 1
          )

Argumen --exit-code memerintahkan Git untuk keluar dengan kode status 1 jika terdeteksi perubahan antara rute yang di-commit dengan rute yang digenerate oleh CI. Pull Request akan otomatis diblokir sampai developer melakukan sinkronisasi.

5. Optimasi Build & Route Filtering

Mengekspor seluruh rute Laravel ke file JavaScript berisiko membocorkan rute internal, API internal, atau endpoint admin ke publik. Konfigurasikan filtering rute di config/ziggy.php untuk membatasi rute yang dimasukkan ke bundle:

<?php

return [
    // Hanya sertakan rute yang relevan untuk Inertia frontend
    'only' => ['app.*', 'auth.*', 'public.*'],

    // Atau kecualikan rute sensitif
    'except' => ['admin.*', 'horizon.*', 'telescope.*', '_debugbar.*'],
];

Jika filter ini diterapkan, eksekusi php artisan ziggy:generate --types akan mereduksi ukuran file JavaScript dan menyaring deklarasi tipe TypeScript, sehingga auto-complete IDE hanya menyarankan rute yang aman untuk diakses oleh frontend.