Laravel Precognitionとは?Vue+Inertiaでリアルタイムバリデーションを実装してみた
いつもご利用ありがとうございます。
この記事には広告が掲載されており、その広告費によって運営しています。
Laravel公式機能「Precognition」の仕組みと、Vue+Inertia構成で実際にリアルタイムバリデーションフォームを実装・検証した記録です。
関連動画
質問やフィードバック
この記事や動画に関する質問やフィードバックあれば、動画のコメント欄にてお気軽にコメントしてください。
ざっくり Precognition
フォームを実際には送信・保存せずに、バックエンドのバリデーションだけを先読み実行できる仕組みです。
SPA でよくある「フロント用の入力チェック」と「バックエンドのバリデーションルール」の二重管理を避けつつ、メールアドレスの重複チェックのような DB を見ないと判定でき ないルールまで、リアルタイムでフォームに反映できます。
Precognition とは?
Precognition は Laravel 本体に組み込まれている機能で、追加パッケージなしでバックエンド側は使えます。
通常のバリデーションは
- JavaScript などで入力を監視して無効の入力があれば表示
- 入力を送信 → サーバーで検証 → エラーがあれば返す
という、フロントとバックエンドを別で実装する流れですが、Precognition を使うと、入力中に「今のところこのフィールドは有効か」だけをサーバーに問い合わせられます。
ポイントは、実際に使っている FormRequest のルールをそのまま使い回せることです。
フロント側で同じルールを再実装する必要がなく、unique のような DB 参照が必要なルールも、先読みの時点で本物の検証が走ります。
とても楽ですね。
実際に環境を作って試してみた
公式ドキュメントを読むだけでなく、Docker 上に Laravel + Vue + Inertia のスターターキットで環境を作り、実際に動くフォームを 1 つ実装してみました。
1. Vue + Inertia スターターキットでプロジェクト作成
laravel new laravel-precognition-demo --vue --database=sqlite --no-authentication --pest執筆時点で Laravel 13.26.1 + Inertia 3 系がインストールされました。
2. バックエンド側は追加パッケージ不要だった
必要だったのは、bootstrap/app.php で web ミドルウェアグループに HandlePrecognitiveRequests を追加するだけです。
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests; //追加
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [
HandleInertiaRequests::class,
AddLinkHeadersForPreloadedAssets::class,
HandlePrecognitiveRequests::class, //追加
]);
})vendor/laravel/framework の中身を見てみると、Illuminate\Foundation\Http\FormRequest に Precognition 対応のコードが既に組み込まれていたため、これで OK です。
3. イベント参加申し込みフォームを実装
検証用に、名前・メールアドレス・参加人数を登録する簡単なフォームを作りました。
unique のバリデーションは、フロント側だけでは絶対に判定できないので、Precognition の効果が分かりやすいと思いこんな感じにしてみました。
// app/Http/Requests/StoreSignupRequest.php
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'max:255', 'unique:signups,email'],
'guests' => ['required', 'integer', 'min:1', 'max:5'],
];
}コントローラーとルートはごく普通の Laravel + Inertia の実装です。
// app/Http/Controllers/SignupController.php
public function store(StoreSignupRequest $request): RedirectResponse
{
Signup::create($request->validated());
return redirect()->route('signup.create')->with('success', 'お申し込みありがとうございます。');
}4. フロント側パッケージを導入する
npm install laravel-precognition-vue-inertia --legacy-peer-deps--legacy-peer-depsを付けた理由
npm install laravel-precognition-vue-inertiaと入れようとしたところ、npm install が失敗しました。
npm error Found: @inertiajs/[email protected]
npm error Could not resolve dependency:
npm error peer @inertiajs/vue3@"^1.0.0 || ^2.0.0" from [email protected]バージョン不整合に遭遇しました。
執筆時点の最新版 [email protected] は、peerDependencies が @inertiajs/vue3 の v1・v2 までしか許可しておらず、現行のスターターキットが使う Inertia v3 とは正式には噛み合っていないようです。
--legacy-peer-deps を付ければインストール自体は通り、後述の通り動作も問題なかったので、今のところ実害はなさそうですが、Precognition の Vue 用ヘルパーが Inertia v3 に追随できていない、という状態は把握しておいた方がよさそうです。
Inertia を使っているか、従来の axios を使っているか
Precognition のフロント用ヘルパーは、
- Vue 単体向けの
laravel-precognition-vue - Vue + Inertia 向けの
laravel-precognition-vue-inertia
の 2 種類があります。
今回は Inertia を使う構成なので、後者を選びましたが、環境によっては、前者を選ぶ必要があります。
5. Vue 側の実装
useForm を Precognition 版に差し替え、各入力欄の change イベントで form.validate() を呼ぶだけで、リアルタイムバリデーションが動きます。
<script setup lang="ts">
//import { useForm } from "@inertiajs/vue3"; 通常のInertiaはコレ
import { useForm } from "laravel-precognition-vue-inertia"
const form = useForm("post", "/signup", {
name: "",
email: "",
guests: 1,
})
</script>
<template>
<input v-model="form.email" @change="form.validate('email')" />
<p v-if="form.invalid('email')">{{ form.errors.email }}</p>
</template>form.invalid('email') でエラーの有無を判定でき、form.errors に FormRequest 側のバリデーションメッセージがそのまま入ってくるので、以下でバリデーション内容をユーザーに表示しています。
<p v-if="form.invalid('email')">{{ form.errors.email }}</p>実際に動かして検証
php artisan serve でサーバーを立ち上げ、ヘッドレスブラウザで実際のフォーム操作を再現しながらスクリーンショットを撮りました。
初期状態です。
不正な形式のメールアドレスを入力してフォーカスを外すと、送信していないのに即座にエラーが表示されます。
事前に登録しておいた既存のメールアドレスを入力すると、こちらもリアルタイムで「既に登録されています」エラーが出ます。
これはフロント側の入力チェックだけでは絶対に再現できない、DB への実問い合わせが伴う検証です。
未登録のメールアドレスに直すと、エラーが消えます。
そのまま送信すると、通常どおり DB に保存され、成功メッセージが表示されました。
実際のリクエスト・レスポンスを見てみた
ブラウザの通信を直接ログに取って、Precognition 用のヘッダーがどう使われているか確認しました。
| 操作 | Precognition-Validate-Only(送信) | ステータス | Precognition-Success(応答) |
|---|---|---|---|
| 名前だけ入力 | name | 204 | true |
| 不正な形式のメールを入力 | email,name | 422 | (なし) |
| 登録済みメールを入力 | email,name | 422 | (なし) |
| 未登録メールに修正 | email,name | 204 | true |
| 送信(通常の POST) | - | 302 | - |
Precognition-Validate-Only には、今回触ったフィールドだけでなく、それまでに触ったフィールドも累積してカンマ区切りで入ってくることが分かりました(name を触った後に email を触ると email,name になる)。
エラーがなければ本文なしの 204、エラーがあれば通常のバリデーションエラーと同じ形式で 422 が返ってきます。
気づいたこと・注意点
デバウンスは既定 1500ms
「デバウンス」は、入力のたびに毎回サーバーへリクエストを送るのではなく、一定時間操作が止まってから、まとめて 1 回だけ送る仕組みです。
laravel-precognition パッケージの中身(validator.js)を見ると、debounceTimeoutDuration = 1500 がデフォルト値でした。
以下のように、form.setValidationTimeout()で変更が可能です。
<script setup lang="ts">
import { useForm } from 'laravel-precognition-vue-inertia';
const form = useForm('post', '/signup', {
name: '',
email: '',
guests: 1,
});
form.setValidationTimeout(300); // ← ここ。formを作った直後に1回呼ぶ
</script>authorize() も一緒に実行される
公 式ドキュメントによると、Precognition のリクエストでも FormRequest の authorize() は通常どおり評価されるとのことです。
バリデーションは実質二重に走る
先読み時点と実送信時点、どちらも本物の FormRequest を通るため、DB を見る unique などのルールはサーバー負荷が増えます。
頻繁に叩かれるフィールドに重いルールを付けるときは要注意です。
フロント用の公式パッケージは Inertia v3 に未対応(執筆時点)
前述の通り [email protected] の peerDependencies は Inertia v1/v2 までのため、最新のスターターキット構成では --legacy-peer-deps が必要でした。
まとめ
とても便利だと思いました。
ユーザーの UX を考えると、送信を押す前にバリデーションを判定したほうが優しいので頑張ってフロントでもバックエンドと整合性をあわせて実装していましたが、
FormRequestの1箇所に書いたルールを使い回してフロントエンドのバリデーションも実装できるのでよかったです。
特に大幅な変更もいらないですし、既存のプロジェクトにも簡単に入れることができると思います。
axios を使ったちょっと前のプロジェクトにも入れられるので良いですね。
バージョン追随がやや遅れているため、最新の Laravel で使う場合は注意をしてみてください。
最後まで読んでいただきありがとうございました。

この記事を書いた人
まっつん
エンジニア歴6年。普段はサーバーサイド開発が中心ですが、個人でスタートアップのサービスを1人で開発・運用しており、Laravel・JavaScript・AWSを中心にフルスタックで対応しています。2019年から、実務でぶつかった課題や解決策をこのブログで発信しています。




