mcpField
H
handling-validation-errors

handling-validation-errors

github.com/zaggino開発349171 回インストール5 日前に更新
リポジトリ

handling-validation-errors について

z-schema の検証エラーを扱う開発者向けスキルです。エラー詳細の捕捉、確認、絞り込み、変換を行い、プログラム処理やフォーム表示に活用する方法を説明します。

主な機能

  1. 01例外または安全モードでエラーを捕捉
  2. 02ネストしたエラー詳細の走査
  3. 03パスのフォームフィールドへの対応付け
  4. 04エラーコードによる絞り込み
  5. 05エラーメッセージのカスタマイズ

活用シーン

  1. 01フォームでのスキーマエラー表示
  2. 02ネストした anyOf や oneOf エラーの処理
  3. 03選択した検証エラーのみの表示
  4. 04最初のエラーで検証を停止

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/zaggino/z-schema --skill handling-validation-errors

インストール後、Claude Code で/handling-validation-errorsを入力して読み込まれたか確認します。

詳細

z-schema での検証エラーの処理

z-schema は、.details 配列に SchemaErrorDetail を含む ValidateError オブジェクトとして検証エラーを報告します。このスキルでは、これらのエラーの確認、絞り込み、マッピング、表示方法について説明します。

エラーの構造

import { ValidateError } from 'z-schema';
import type { SchemaErrorDetail } from 'z-schema';

ValidateError は Error を継承します。

プロパティ型説明
.namestring常に 'ValidateError' です
.messagestring概要メッセージ
.detailsSchemaErrorDetail[]個々のエラーすべて

各 SchemaErrorDetail:

フィールド型説明
messagestring人が読めるテキスト。例:"Expected type string but found type number"
codestring機械可読なコード。例:"INVALID_TYPE"
params(string | number | Array)[]メッセージテンプレートのプレースホルダーに入る値
pathstring | Array<string | number>エラーが発生した値への JSON Pointer("#/age" または ["age"])
keywordstring?エラーの原因となったスキーマキーワード("type"、"required" など)
innerSchemaErrorDetail[]?コンビネーター(anyOf、oneOf、not)によるサブエラー
schemaPathArray<string | number>?制約までのスキーマ内のパス
schemaIdstring?スキーマ ID がある場合
titlestring?スキーマの title がある場合
descriptionstring?スキーマの description がある場合

エラーの捕捉

try/catch(デフォルトモード)
import ZSchema from 'z-schema';

const validator = ZSchema.create();

try {
  validator.validate(data, schema);
} catch (err) {
  if (err instanceof ValidateError) {
    for (const detail of err.details) {
      console.log(`[${detail.code}] ${detail.path}: ${detail.message}`);
    }
  }
}
セーフモード(try/catch なし)
const validator = ZSchema.create();
const { valid, err } = validator.validateSafe(data, schema);

if (!valid && err) {
  for (const detail of err.details) {
    console.log(`[${detail.code}] ${detail.path}: ${detail.message}`);
  }
}

ネストされたエラーの走査

コンビネーター(anyOf、oneOf、not)は、ネストされた inner エラーを生成します。再帰的なウォーカーを使うと、深さを問わず処理できます。

function walkErrors(details: SchemaErrorDetail[], depth = 0): void {
  for (const detail of details) {
    const indent = '  '.repeat(depth);
    console.log(`${indent}[${detail.code}] ${detail.path}: ${detail.message}`);
    if (detail.inner) {
      walkErrors(detail.inner, depth + 1);
    }
  }
}

const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
  walkErrors(err.details);
}
すべてのリーフエラーを収集する

コンビネーターのラッパーをスキップしてツリーを平坦化し、具体的なエラーをすべて取得します。

function collectLeafErrors(details: SchemaErrorDetail[]): SchemaErrorDetail[] {
  const leaves: SchemaErrorDetail[] = [];
  for (const detail of details) {
    if (detail.inner && detail.inner.length > 0) {
      leaves.push(...collectLeafErrors(detail.inner));
    } else {
      leaves.push(detail);
    }
  }
  return leaves;
}

エラーをフォームフィールドにマッピングする

JSON Pointer のパスをフィールド名に変換し、UI フォームの検証に使用します。

function pathToFieldName(path: string | Array<string | number>): string {
  if (Array.isArray(path)) {
    return path.join('.');
  }
  // JSON Pointer string: "#/address/city" → "address.city"
  return path.replace(/^#\/?/, '').replace(/\//g, '.');
}

function errorsToFieldMap(details: SchemaErrorDetail[]): Record<string, string[]> {
  const map: Record<string, string[]> = {};
  const leaves = collectLeafErrors(details);
  for (const detail of leaves) {
    const field = pathToFieldName(detail.path) || '_root';
    (map[field] ??= []).push(detail.message);
  }
  return map;
}

// Usage
const { valid, err } = validator.validateSafe(formData, schema);
if (!valid && err) {
  const fieldErrors = errorsToFieldMap(err.details);
  // { "email": ["Expected type string but found type number"],
  //   "age": ["Value 150 is greater than maximum 120"] }
}
配列パスを使用する

プログラムからアクセスしやすくするには、reportPathAsArray を有効にします。

const validator = ZSchema.create({ reportPathAsArray: true });
const { valid, err } = validator.validateSafe(data, schema);
// err.details[0].path → ["address", "city"] instead of "#/address/city"

エラーの絞り込み

呼び出しごとの絞り込み

3 番目の引数として includeErrors または excludeErrors を渡します。

// Only report type mismatches
validator.validate(data, schema, { includeErrors: ['INVALID_TYPE'] });

// Suppress string-length errors
validator.validate(data, schema, { excludeErrors: ['MIN_LENGTH', 'MAX_LENGTH'] });
プログラムによる事後フィルタリング
const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
  const typeErrors = err.details.filter((d) => d.code === 'INVALID_TYPE');
  const requiredErrors = err.details.filter((d) => d.code === 'OBJECT_MISSING_REQUIRED_PROPERTY');
}

カスタムエラーメッセージ

エラーコードをユーザーフレンドリーなメッセージにマッピングします。

const friendlyMessages: Record<string, (detail: SchemaErrorDetail) => string> = {
  INVALID_TYPE: (d) => `${pathToFieldName(d.path)} must be a ${d.params[0]}`,
  OBJECT_MISSING_REQUIRED_PROPERTY: (d) => `${d.params[0]} is required`,
  MINIMUM: (d) => `${pathToFieldName(d.path)} must be at least ${d.params[1]}`,
  MAXIMUM: (d) => `${pathToFieldName(d.path)} must be at most ${d.params[1]}`,
  MIN_LENGTH: (d) => `${pathToFieldName(d.path)} must be at least ${d.params[1]} characters`,
  MAX_LENGTH: (d) => `${pathToFieldName(d.path)} must be at most ${d.params[1]} characters`,
  PATTERN: (d) => `${pathToFieldName(d.path)} has an invalid format`,
  ENUM_MISMATCH: (d) => `${pathToFieldName(d.path)} must be one of the allowed values`,
  INVALID_FORMAT: (d) => `${pathToFieldName(d.path)} is not a valid ${d.params[0]}`,
  ARRAY_UNEVALUATED_ITEMS: () => `Array contains items not covered by any schema`,
  OBJECT_UNEVALUATED_PROPERTIES: (d) => `${d.params[0]} is not allowed by the schema`,
  COLLECT_EVALUATED_DEPTH_EXCEEDED: (d) => `Schema nesting too deep (max ${d.params[0]})`,
  MAX_RECURSION_DEPTH_EXCEEDED: (d) => `Recursion depth exceeded (max ${d.params[0]})`,
};

function toFriendlyMessage(detail: SchemaErrorDetail): string {
  const fn = friendlyMessages[detail.code];
  return fn ? fn(detail) : detail.message;
}

最初のエラーで停止する

フェイルファストが必要な場合に使用します。

const validator = ZSchema.create({ breakOnFirstError: true });

これにより、最初に検出されたエラーのみが報告され、反復的な修正時のノイズを減らせます。

keyword フィールドの使用

keyword フィールドは、エラーの原因となったスキーマキーワードを示します。プログラムでエラーを分類する際に便利です。

const { valid, err } = validator.validateSafe(data, schema);
if (!valid && err) {
  for (const detail of err.details) {
    switch (detail.keyword) {
      case 'required':
        // handle missing field
        break;
      case 'type':
        // handle type mismatch
        break;
      case 'format':
        // handle format failure
        break;
    }
  }
}

参考ファイル

説明付きのエラーコード一覧については、validating-json-data スキルのエラーコードリファレンスを参照してください。

コメント