サーバーエラー統合
15.3.1. バックエンドエラーレスポンス形式
TQSバックエンド標準エラーレスポンスは以下の構造に従います。フロントエンドはこの形式に合わせてエラーを処理しなければなりません。
typescript
// src/types/api.ts
interface ApiErrorResponse {
errorCode: string // エラーコード(例:'VALIDATION_ERROR', 'CONFLICT')
message: string // ユーザー向けメッセージ
fieldErrors?: { // フィールドごとの検証エラー(任意)
field: string
message: string
}[]
}| フィールド | 必須有無 | 説明 |
|---|---|---|
errorCode | 必須 | エラー分類コード |
message | 必須 | グローバルエラーメッセージ |
fieldErrors | 任意 | フィールド単位の検証失敗リスト |
fieldErrorsはフォーム送信時にサーバー側の検証が失敗した場合にのみ含まれます。errorCodeはフロントエンドでエラー種別の分岐処理に使用します。
15.3.2. フィールドごとのエラーマッピング
サーバーから返されたfieldErrorsはフォームのerrors状態にマッピングします。
typescript
// src/utils/mapServerErrors.ts
import type { ApiErrorResponse } from '@/types/api'
export function mapFieldErrors(
response: ApiErrorResponse
): Record<string, string> {
const errors: Record<string, string> = {}
if (response.fieldErrors) {
for (const fieldError of response.fieldErrors) {
errors[fieldError.field] = fieldError.message
}
}
return errors
}フォーム送信ハンドラーで以下のように適用します。
typescript
import { mapFieldErrors } from '@/utils/mapServerErrors'
import axios from 'axios'
async function handleSubmit() {
isSubmitting.value = true
errors.value = {}
try {
await createUser(form.value)
} catch (e) {
if (axios.isAxiosError(e) && e.response?.data) {
const serverError = e.response.data as ApiErrorResponse
errors.value = mapFieldErrors(serverError)
}
} finally {
isSubmitting.value = false
}
}- サーバーの
field値はフォームオブジェクトのキーと一致しなければなりません。 - バックエンドとフロントエンド間のフィールド名規則を事前に合意します。
15.3.3. グローバルエラー処理
fieldErrorsにマッピングされないエラーはグローバルエラーとして処理します。トースト通知またはフォーム上部のバナーで表示します。
typescript
import { useToast } from '@/composables/useToast'
const { showToast } = useToast()
async function handleSubmit() {
isSubmitting.value = true
errors.value = {}
try {
await createUser(form.value)
} catch (e) {
if (axios.isAxiosError(e) && e.response?.data) {
const serverError = e.response.data as ApiErrorResponse
const fieldErrors = mapFieldErrors(serverError)
if (Object.keys(fieldErrors).length > 0) {
errors.value = fieldErrors
} else {
showToast({ type: 'error', message: serverError.message })
}
} else {
showToast({ type: 'error', message: 'リクエスト処理中にエラーが発生しました。' })
}
} finally {
isSubmitting.value = false
}
}| エラー種別 | 表示方式 | 例 |
|---|---|---|
| フィールド検証エラー | フィールド下部にインライン | 「既に使用されているメールアドレスです。」 |
| ビジネスロジックエラー | トースト通知 | 「1日の登録上限を超過しました。」 |
| ネットワーク/サーバーエラー | トースト通知 | 「リクエスト処理中にエラーが発生しました。」 |
15.3.4. エラーの初期化
ユーザーがフォームを修正すると、関連するエラーを自動的に初期化します。再送信時にはすべてのエラーを初期化します。
typescript
import { watch } from 'vue'
// 個別フィールド変更時に該当エラーを初期化
watch(
() => form.value.email,
() => {
if (errors.value.email) {
delete errors.value.email
}
}
)
// 汎用初期化 — フォーム全体を監視
watch(
form,
() => {
errors.value = {}
},
{ deep: true }
)- フィールド単位の初期化は特定フィールドのエラーのみを削除し、他のフィールドのエラーメッセージを維持します。
- フォーム全体の監視はすべてのエラーを一括削除するため簡潔ですが、他のフィールドのエラーも消える点に注意します。
- プロジェクト要件に応じて適切な戦略を選択します。