Laravel Wayfinder: типобезопасные маршруты для TypeScript-фронтенда на практике — PROG-TIME

Laravel Wayfinder: типобезопасные маршруты для TypeScript-фронтенда на практике

10.06.2026

Если вы пишете фронтенд на TypeScript поверх Laravel, то наверняка сталкивались с вечной болью: URL-адреса маршрутов приходится либо хардкодить строками, либо тащить через Ziggy и надеяться, что имена маршрутов на бэкенде и фронтенде не разъедутся. Переименовали контроллер или поменяли сегмент URI — и узнаёте об этом только в рантайме, когда что-то ломается. Laravel Wayfinder решает эту проблему радикально: он генерирует полностью типизированные TypeScript-функции для ваших контроллеров и маршрутов, которые можно импортировать и вызывать как обычные функции. В этой статье разберём, как установить Wayfinder, настроить генерацию и применять его на практике.

Что такое Wayfinder и какую проблему он решает

Wayfinder — это официальный first-party пакет от команды Laravel, который связывает бэкенд на PHP и фронтенд на TypeScript без лишнего трения. Он анализирует ваше приложение и автоматически создаёт TypeScript-эквиваленты для маршрутов и методов контроллеров. В результате вы вызываете эндпоинты Laravel прямо в клиентском коде, как любую другую функцию — с автодополнением, проверкой типов параметров и без ручной синхронизации.

На момент написания статьи Wayfinder находится в стадии Beta (последний релиз — v0.1.20 от мая 2026 года), и API может меняться вплоть до выхода стабильной версии v1.0.0. Тем не менее пакет уже достаточно зрелый: начиная с Laravel 12 он заменил Ziggy в официальных стартовых наборах (starter kits). Так что знакомиться с ним стоит уже сейчас.

Установка

Сначала ставим сам пакет через Composer:

composer require laravel/wayfinder

Дальше нужен Vite-плагин, чтобы определения автоматически генерировались при сборке и при изменении файлов во время работы dev-сервера. Ставим его через NPM:

npm i -D @laravel/vite-plugin-wayfinder

После этого подключаем плагин в vite.config.js:

import { wayfinder } from "@laravel/vite-plugin-wayfinder";

export default defineConfig({
    plugins: [
        wayfinder(),
        // ...
    ],
});

Теперь при каждом запуске сборки и при изменении маршрутов или контроллеров Wayfinder будет перегенерировать TypeScript-определения автоматически.

Генерация определений

За генерацию отвечает Artisan-команда:

php artisan wayfinder:generate

По умолчанию Wayfinder создаёт файлы в трёх директориях внутри resources/js: wayfinder, actions и routes. Базовый путь можно переопределить:

php artisan wayfinder:generate --path=resources/js/wayfinder

Если вам не нужны определения для методов контроллеров или для маршрутов, отдельные части генерации можно пропустить:

php artisan wayfinder:generate --skip-actions
php artisan wayfinder:generate --skip-routes

Сгенерированные директории wayfinder, actions и routes можно смело добавлять в .gitignore — они полностью пересоздаются на каждой сборке, так что хранить их в репозитории нет смысла.

Использование в клиентском коде

Функции Wayfinder возвращают объект с готовым URL и HTTP-методом по умолчанию:

import { show } from "@/actions/App/Http/Controllers/PostController";

show(1); // { url: "/posts/1", method: "get" }

Если нужен только URL или другой метод из объявленных на сервере, можно вызвать дополнительные методы на сгенерированной функции:

import { show } from "@/actions/App/Http/Controllers/PostController";

show.url(1);  // "/posts/1"
show.head(1); // { url: "/posts/1", method: "head" }

Аргументы можно передавать в разных формах — это удобно, когда у вас на руках то отдельный id, то целый объект модели:

import { show, update } from "@/actions/App/Http/Controllers/PostController";

// Один параметр...
show(1);
show({ id: 1 });

// Несколько параметров...
update([1, 2]);
update({ post: 1, author: 2 });
update({ post: { id: 1 }, author: { id: 2 } });

Если в маршруте задан ключ привязки (например, /posts/{post:slug}), Wayfinder это распознает и позволит передать значение через свойство объекта:

import { show } from "@/actions/App/Http/Controllers/PostController";

// Маршрут /posts/{post:slug}...
show("my-new-post");
show({ slug: "my-new-post" });

Для invokable-контроллеров импортированную функцию можно вызывать напрямую, а при импорте контроллера целиком — обращаться к его методам как к свойствам объекта:

import StorePostController from "@/actions/App/Http/Controllers/StorePostController";
StorePostController();

import PostController from "@/actions/App/Http/Controllers/PostController";
PostController.show(1);

Обратите внимание: при импорте контроллера целиком он не подвергается tree-shaking, и в финальный бандл попадут все его экшены. Если важен размер сборки, импортируйте отдельные методы.

Именованные маршруты и формы

Помимо экшенов контроллеров, Wayfinder умеет генерировать методы и для именованных маршрутов:

import { show } from "@/routes/post";

// Именованный маршрут `post.show`...
show(1); // { url: "/posts/1", method: "get" }

Если приложение использует обычную отправку HTML-форм, Wayfinder поможет и здесь. Сначала включаем генерацию form-вариантов:

php artisan wayfinder:generate --with-form

Теперь у каждой функции появляется вариант .form, который раскладывается в атрибуты тега <form>. Метод вроде PATCH автоматически подставляется через скрытое поле _method:

import { store, update } from "@/actions/App/Http/Controllers/PostController";

const Page = () => (
    <form {...store.form()}>
        {/* <form action="/posts" method="post"> */}
    </form>
);

const EditPage = () => (
    <form {...update.form(1)}>
        {/* <form action="/posts/1?_method=PATCH" method="post"> */}
    </form>
);

Query-параметры и интеграция с Inertia

Каждый метод Wayfinder принимает финальный необязательный аргумент options, куда можно передать объект query для добавления query-параметров:

import { show } from "@/actions/App/Http/Controllers/PostController";

const options = { query: { page: 1, sort_by: "name" } };

show(1, options);     // { url: "/posts/1?page=1&sort_by=name", method: "get" }
show.url(1, options); // "/posts/1?page=1&sort_by=name"

Если нужно слить новые параметры с уже существующими в URL, используйте mergeQuery. Чтобы удалить параметр, задайте его значение как null или undefined.

Отдельно радует связка с Inertia. Результат метода Wayfinder можно передать прямо в submit у useForm — URL и метод подставятся сами:

import { useForm } from "@inertiajs/react";
import { store } from "@/actions/App/Http/Controllers/PostController";

const form = useForm({ name: "My Big Post" });

form.submit(store()); // POST на `/posts`...

Точно так же Wayfinder работает с компонентом Link из Inertia — просто передайте результат функции в проп href.

Важный нюанс при деплое

Wayfinder читает маршруты из зарегистрированного роутера. Если Laravel поднимается с закешированной таблицей маршрутов от предыдущего релиза, генерация отработает по устаревшим данным: новые маршруты молча не попадут в директории routes/ и actions/, а Vite упадёт с ошибкой вида Could not load resources/js/routes/<name>. Если ваш скрипт деплоя выполняет php artisan optimize или route:cache, обязательно сбросьте кеш перед сборкой:

php artisan route:clear
npm run build

Итоги

Wayfinder убирает целый класс ошибок, связанных с рассинхронизацией маршрутов между бэкендом и фронтендом. Вместо хрупких строк с URL вы получаете типизированные функции с автодополнением, которые ломают сборку (а не прод), если на бэкенде что-то изменилось. Установка занимает пару команд, интеграция с Vite и Inertia сделана аккуратно, а поддержка форм и query-параметров закрывает большинство реальных сценариев. Единственное, о чём стоит помнить — пакет пока в Beta, поэтому перед обновлением версий заглядывайте в changelog. Но учитывая, что Wayfinder уже стал частью официальных стартовых наборов Laravel 12, инвестиция в его освоение точно окупится.