Skip to content

refactor: ajvをvalibotに移行する #17806

Description

@kakkokari-gtyih

Summary

バックエンドの API パラメータ検証・レスポンス型定義・OpenAPI (api.json) 生成に使われているスキーマ基盤を、AJV + 独自拡張 json-schema (optional / nullable / ref / selfRef + SchemaType<> 型変換) から Valibot に移行する。

現行の仕組みは 1 つの as const スキーマオブジェクトが TS 型導出 / AJV 検証 / OpenAPI 生成 / misskey-js 型生成 の 4 役を担う設計で強力だが、独自拡張と型レベル変換 (SchemaType<>) の複雑さが保守・開発体験のボトルネックになっている。Valibot なら「スキーマ = バリデータ = 型 = spec」をValibotの標準機能だけで実現でき、独自基盤を撤去できる。

Purpose

解決したい問題

  1. 型パズルの保守コスト: SchemaType<> は excessive stack depth (Excessive stack depth comparing types 'SchemaTypeDef<?>' and 'SchemaTypeDef<?>' #8535) 回避のための UnionToIntersection 等を含み、スキーマを深くすると型推論が壊れる可能性がある
  2. 独自拡張の学習コスト: optional / nullable / ref / selfRef は JSON Schema 標準に無い独自仕様で、AJV からは未知キーワードとして無視される (paramDef と res で意味が変わる暗黙の規約もある)。新規コントリビュータの参入障壁にもなりうる
  3. 型と実態の乖離が隠れる: { type: 'object' } (properties 無し) 等が TS 上 any に潰れ、スキーマと実装の乖離がコンパイルでも実行時でも検出されない
  4. エラー情報の貧弱さ: 検証失敗時は先頭 1 件の schemaPath (#/properties/x/type 形式) しか返らず、クライアントがどのパラメータの問題か特定しにくい (APIのrequired paramがanyOfな際のreasonメッセージの改善 #10928)

移行で得られる利点

  • 型チェックの高速化: SchemaType<> の再帰条件型が消え、backend の typecheck の時間短縮が見込める (instantiations は増えるが再帰が浅くなる)
  • DX(Developer Experience) の改善:
    • as const や型注釈のボイラープレートが不要になり、v.InferOutput で型が直接得られる
    • entity 参照が ref から直接 import になり、定義ジャンプ・補完・リネームが機能する
    • 入出力型の分離: v.optional(x, default) で「クライアントが送る型 (省略可)」と「ハンドラが受け取る型 (default 適用済み)」が自動で分かれる
    • v.variant による判別 union、.entries spread による合成など、標準機能でスキーマを構成できる
  • 検証エラーの改善: dot-path (poll.choices.1 形式) + 全エラーの詳細を返せる (APIのrequired paramがanyOfな際のreasonメッセージの改善 #10928, エラーメッセージのUIを良い感じにしたい #6322)
  • 隠れていたバグの顕在化: any に潰れていた型の陰に隠れていたスキーマと実装の乖離・戻り型が違う箇所を検出可能。res スキーマが実行可能なバリデータになるため、将来的に開発環境でのレスポンス検証 (スキーマドリフト検出) も可能になる
  • spec の正確化: エラーレスポンスの実 HTTP ステータスコード反映 (404 等)、「required と宣言しながら実際は省略可」だったパラメータの是正など、OpenAPI spec を実態に一致させられる
  • コード削減と依存整理: スキーマ関連コードの削減や、valibot(軽量・モジュラーで tree-shaking 前提の設計)への移行でバックエンドのコード量を削減することが可能(なはず)

注意点

  • v.object は未知キーを出力から除去する (AJV は素通し)。宣言外キーに依存するハンドラが無いことは移行時に全数確認が必要
  • AJV の minLength/maxLength は Unicode コードポイント数、Valibot 標準は UTF-16 単位のため、公式に実装されるまではコードポイント数でカウントするカスタムアクションが必要 → maxCodePoints / minCodePoints (UTF-32 code points) open-circle/valibot#875
  • anyOf の検証が AJV の部分検証より厳密になるケースがある

Do you want to implement this feature yourself?

  • Yes, I will implement this by myself and send a pull request

Metadata

Metadata

Assignees

No one assigned

    Labels

    ✨FeatureThis adds/improves/enhances a feature💚RefactorRewriting code without changing behavior

    Type

    No type

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions