Nuxt Precognition
これは nuxt-laravel-precognition の新しいバージョンです。同じ機能を提供しますが、Laravel に依存しません。
$fetch と Laravel のみをサポートする代わりに、基本的な Precognition プロトコルを実装する任意のバックエンドを対象とした、シンプルな Promise で動作します。これらの Promise は、フォーム payload
とプロトコル Headers
を受け取ります。
例
interface User = {
email: string
password: string
}
const form = useForm(
(): User => ({ email: '', password: '' }),
(body, headers) => $fetch('/api/login', { method: 'POST', headers, body })
)
このモジュールにはネイティブの Nitro 統合が付属していますが、他のバックエンドでも動作します。
Lambda のみを使用していますか?Lambda Precognition でカバーされます!!
任意のバリデーションライブラリ (Zod と言った人は誰ですか?) をサーバー側またはクライアント側でサポートします。特定の Error parsers
を設定するだけで済みます。
機能
- Laravel に準拠
- バリデーションライブラリに依存しない
- クライアント側とサーバー側のバリデーション
- 最適な TypeScript サポート
- 高度なカスタマイズ性
仕組み
すべては errorParsers
(エラー payload
からバリデーションエラーを読み取るためのユーザー定義関数) を中心に展開します。
type ValidationErrors = Record<string, string | string[]>
interface ValidationErrorsData {
message: string
errors: ValidationErrors
}
type ValidationErrorParser = (error: Error) => ValidationErrorsData | undefined | null
これらはグローバル ( Nuxt Plugin
内またはカスタム eventHandler
) で、または form
インスタンスごとに定義できます。
Zod を使用しているとしましょう。
nuxt プラグインを作成し、「Zod エラーパーサー」を定義するだけです。
// plugins/precognition.ts
export default defineNuxtPlugin(() => {
const { $precognition } = useNuxtApp()
$precognition.errorParsers.push(
(error) => {
if (error instanceof ZodError) {
const errors = {} as Record<string, string[]>
error.errors.forEach((e) => {
const key = e.path.join('.')
if (key in errors) {
errors[key].push(e.message)
return
}
errors[key] = [e.message]
})
return { errors, message: 'Validation error' }
}
return null
},
)
})
これからは、useForm
がエラーをキャッチするたびに、パーサーが実行され、バリデーションエラーをキャプチャして割り当てます。
複数のページで同じオプションを再利用する場合は、useForm.create
ファクトリ関数を使用して、カスタムコンポーザブルを作成できます。
サーバー側はどうですか
同じ考えで、nitro プラグインを作成します
// server/plugins/precognition.ts
import { ZodError } from 'zod'
export default defineNitroPlugin((nitroApp) => {
nitroApp.hooks.hook('request', (event) => {
event.context.$precognition.errorParsers = [
(error) => {
if (error instanceof ZodError) {
const errors: Record<string, string[]> = {}
error.errors.forEach((e) => {
const key = e.path.join('.')
if (key in errors) {
errors[key].push(e.message)
return
}
errors[key] = [e.message]
})
const message = error.errors.at(0)?.message ?? 'Validation error'
return { errors, message }
}
},
]
})
})
すべてのリクエストにフックするのが好きでない場合は、definePrecognitiveEventHandler.create
ファクトリ関数を使用して、カスタム eventHandler を作成できます。
definePrecognitiveEventHandler
の onRequest
ハンドラー内でバリデーションロジックを作成します。
// server/api/login.post.ts
import { z } from 'zod'
import { definePrecognitiveEventHandler, readBody } from '#imports'
const loginSchema = z.object({
email: z.string().email().refine(_email => // Check for email uniqueness
true, { message: 'Email is already in use' },
),
password: z.string(),
}).refine((_data) => {
// Check for email and password match
// ...
return true
},
{ message: 'invalid credentials', path: ['email'] },
)
export default definePrecognitiveEventHandler({
async onRequest(event) {
const body = await readBody(event)
loginSchema.parse(body)
},
handler: () => {
return {
status: 200,
body: {
message: 'Success',
},
}
},
})
今回は、エラーが NuxtServerValidationError
に変換され、nuxt 構成ファイルで事前定義されたパーサーを有効にすると、クライアント側でキャプチャされます。
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-precognitiion'],
precognition: {
backendValidation: true,
enableNuxtClientErrorParser: true,
}
})
ValidationError
をスローするのは、onRequest
ハンドラー内のみ ( オブジェクト表記
を使用) であることを忘れないでください。.
ベースの handler
内のロジックは、precognitiveRequests
中には処理されません。
- 各
event.context
には、リクエストが予知であるかどうかを示すフラグ ({ precognitive: boolean }
) も含まれており、Precognitive ヘッダーの存在を調べています。
予知プロトコル
nitro の外部 (AWS Lambda) で独自のバックエンドロジックを定義する必要がある場合は、次の要件リストを尊重してください。
- 予知リクエストには以下が必要です
- 予知ヘッダー
{ 'Precognitive': 'true' }
- 予知ヘッダー
- 特定の変数を検証するには、各キーを ValidateOnly ヘッダー内で、カンマ区切りでドット表記を利用して指定する必要があります
{ 'Precognition-Validate-Only': 'name,age,address.street,address.number' }
- フォーム全体を検証するには、ValidateOnly ヘッダーを省略するか、空の文字列として定義する必要があります。
- 成功したバリデーション応答には以下が必要です
- 予知ヘッダー
{ 'Precognitive': 'true' }
- 予知成功ヘッダー
{ 'Precognition-Success': 'true' }
- 予知成功ステータスコード:
204
- 予知ヘッダー
- エラーバリデーション応答には以下が必要です
- 予知ヘッダー
{ 'Precognitive': 'true' }
- 必要に応じて ValidationOnly ヘッダー
{ 'Precognition-Validate-Only': 'name,age,address.street,address.number' }
- バリデーションエラー ステータスコード:
422
- バリデーションエラーとメッセージは、定義したロジックに従って、または標準の
errorParsers
を使用して解析されます。- NuxtErrorParsers:
NuxtPrecognitiveErrorResponse
:Response & { _data: { data: ValidationErrorsData }}
- LaravelErrorParsers:
LaravelPrecognitiveErrorResponse
:Response & { _data: ValidationErrorsData }
- NuxtErrorParsers:
- 予知ヘッダー
クイックセットアップ
ワンコマンドで Nuxt アプリケーションにモジュールをインストールします
npx nuxi module add nuxt-precognition
構成
名前 | タイプ | デフォルト | 説明 |
---|---|---|---|
validationTimeout | 数値 | 1500 | 2 つの予知バリデーションリクエスト間のデバウンス時間 (ミリ秒単位)。 |
backendValidation | ブール値 | false | 予知バリデーションを有効にするためのフラグ。 |
validateFiles | ブール値 | false | 予知リクエストでファイルのバリデーションを有効にするためのフラグ。 |
enableNuxtClientErrorParser | ブール値 | false | nuxtErrorParsers をクライアント側 (form.validate および form.submit 内) で有効にするためのフラグ。 |
enableLaravelClientErrorParser | ブール値 | false | laravelErrorParsers をクライアント側 (form.validate および form.submit 内) で有効にするためのフラグ。 |
enableLaravelServerErrorParser | ブール値 | false | laravelErrorParsers をクライアント側 (definePrecognitiveEventHandler 内) で有効にするためのフラグ。 |
ステータスハンドラー
公式パッケージと同様に、特定のエラーコードに対して、グローバルまたはインスタンスレベルでカスタムハンドラーを定義できます。
// plugins/precognition.ts
export default defineNuxtPlugin(() => {
const { $precognition } = useNuxtApp()
$precognition.statusHandlers = {
401: async (error, form) => {
form.error = createError('Unauthorized')
await navigateTo('/login')
},
403: async (error, form) => {
form.error = createError('Forbidden')
},
}
})
以上です!これで Nuxt アプリで Nuxt Precognition を使用できます ✨
Laravel を使用する
- このようなプラグインを定義します
// plugins/api.ts
export default defineNuxtPlugin((app) => {
const { $precognition } = useNuxtApp()
const token = useCookie('XSRF-TOKEN')
const api = $fetch.create({
baseURL: 'https://127.0.0.1',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
onRequest: ({ options }) => {
// Setup csrf protection for every requests if available
if (token.value) {
const headers = new Headers(options.headers)
headers.set('X-XSRF-TOKEN', token.value)
options.headers = headers
}
},
onResponse: (context) => {
// ensure that all precognitive requests will receive precognitive responses
$precognition.assertSuccessfulPrecognitiveResponses(context)
},
})
async function fetchSanctumToken() {
try {
await api('/sanctum/csrf-cookie')
token.value = useCookie('XSRF-TOKEN').value
if (!token.value) {
throw new Error('Failed to get CSRF token')
}
}
catch (e) {
console.error(e)
}
}
app.hook('app:mounted', fetchSanctumToken)
return {
provide: {
api,
sanctum: {
fetchToken: fetchSanctumToken,
token,
},
},
}
})
- バックエンドバリデーションとネイティブの Laravel エラーパーサーをクライアント側またはサーバー側で有効にします
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-precognition'],
precognition: {
backendValidation: true,
enableLaravelClientErrorParser: true,
},
/*
...
*/
})
* enableLaravelServerErrorParser
を有効にする場合は、enableNuxtClientErrorParser
も有効にする必要があります
- Laravel Cors 構成ファイルをセットアップします
// config/cors.php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
*/
'paths' => ['*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [env('FRONTEND_URL', 'https://127.0.0.1:3000')],
'allowed_headers' => ['*'],
'exposed_headers' => ['Precognition', 'Precognition-Success'],
'max_age' => 0,
'supports_credentials' => true,
];
- 必要な場所で Precognition ミドルウェアを有効にします
// routes/api.php
Route::middleware('precognitive')->group(function () {
Route::apiResource('posts', \App\Http\Controllers\PostController::class);
});
貢献
ローカル開発
# Install dependencies
npm install
# Generate type stubs
npm run dev:prepare
# Develop with the playground
npm run dev
# Build the playground
npm run dev:build
# Run ESLint
npm run lint
# Run Vitest
npm run test
npm run test:watch
# Release new version
npm run release