Laravel Wayfinder: Bridging the Gap Between PHP Backends and TypeScript Frontends
Overview: The Quest for End-to-End Type Safety
For years, developers building with Product model or a strict Enum in Laravel, but your frontend remains blissfully unaware, forced to rely on manual type definitions that inevitably drift out of sync with the server.
Prerequisites
To get the most out of this tutorial, you should be comfortable with:
- Laravel 10+: Basic knowledge of routing, controllers, and Form Requests.
- Modern Frontend Frameworks: Familiarity with ReactorVue.js, specifically usingViteas a build tool.
- TypeScript Basics: Understanding how interfaces and types provide editor autocomplete and build-time safety.
- GitHub Actions: Basic knowledge of CI/CD workflows if you plan to sync types across separate repositories.
Key Libraries & Tools
- Surveyor: A "mostly static" analysis tool that inspects your PHP classes, methods, and bindings to extract raw metadata about your app.
- Ranger: A layer above Surveyor that consumes raw data and transforms it into rich, digestible Data Transfer Objects (DTOs).
- Wayfinder Vite Plugin: The client-side companion that watches for backend changes and triggers the regeneration of TypeScriptdefinitions in real-time.
- Laravel Echo: When combined with Wayfinder, it provides type-safe event broadcasting payloads.
Code Walkthrough: Implementing Type-Safe Contracts
1. The Vite Integration
Everything starts with the
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import wayfinder from 'wayfinder-vite-plugin';
export default defineConfig({
plugins: [
laravel(['resources/js/app.ts']),
wayfinder({
// Patterns of files to watch for changes
watch: ['app/Http/Controllers/**', 'app/Models/**']
}),
],
});
2. Auto-Generating Shared Props
In an HandleInertiaRequests middleware to sync these automatically.
// app/Http/Middleware/HandleInertiaRequests.php
public function share(Request $request): array
{
return array_merge(parent::share($request), [
'auth' => [
'user' => $request->user(),
],
'is_admin' => (bool) $request->user()?->admin,
]);
}
On the frontend, Wayfinder performs declaration merging so that the usePage hook knows exactly what is available:
import { usePage } from '@inertiajs/react';
const { props } = usePage();
// TypeScript knows 'is_admin' exists and is a boolean
if (props.is_admin) {
console.log("Access granted");
}
3. Validation via Form Requests
One of the most powerful features in the latest beta is the extraction of validation rules. When you type-hint a FormRequest in your controller, Wayfinder generates a matching
// app/Http/Requests/ProductUpdateRequest.php
public function rules(): array
{
return [
'name' => 'required|string',
'price' => 'required|numeric|min:0',
'description' => 'nullable|string',
];
}
Wayfinder converts these rules into a type you can pass to Inertia's useForm hook, preventing you from sending the wrong data types to the server.
import { useForm } from '@inertiajs/react';
import { ProductUpdateRequest } from '@/types/generated';
const form = useForm<ProductUpdateRequest>({
name: '',
price: 0,
description: null,
});
Syntax Notes: Specificity Matters
Wayfinder relies on the clarity of your PHP code. The more specific your types are in Laravel, the better the nullable in a Form Request, it will correctly append | null to the generated
Practical Example: Jumping the Fence
What happens if your Laravel backend and
This workflow ensures that the frontend team is immediately notified when a route changes or a new field is added to an API response. It turns a manual communication task into a fail-safe automated process.
Tips & Gotchas
- Cashing Issues: During beta, the internal cache of Surveyorcan occasionally become corrupted. If your types aren't reflecting your PHP changes, try clearing your app cache or restarting theVitedev server.
- Performance in Large Apps: Because Wayfinder performs static analysis across your entire codebase, very large applications might experience a slight delay (a few seconds) between saving a PHP file and the TypeScriptserver picking up the change.
- Tree Shaking: Unlike older tools that exported every route into a global object, Wayfinder exports individual route helpers. This allows modern bundlers to "tree-shake" away any routes that aren't actually imported in your frontend code, keeping your production bundles lean.
- Eloquent Resources: Full support for complex
JsonResourcetransformations is still in active development. For the most reliable results, stick toarrayableandjsonableobjects for now.
