Upgrade to Pro — share decks privately, control downloads, hide ads and more …

protovalidate-es を導入してみた

protovalidate-es を導入してみた

TSKaigi2026〜アフターパーティー〜 における、クラウドサイン開発チーム SWE 辻 佳佑(@t0daaay) による登壇資料です。

【TSKaigi2026〜アフターパーティー〜】
https://every.connpass.com/event/393937/

■ 弁護士ドットコム株式会社 CompanyDeck for Engineers
https://speakerdeck.com/bengo4com/2026-companydeck-for-engineers

■ 採用情報
https://hrmos.co/pages/bengo4/jobs

■ テックブログ:弁護士ドットコム CREATORS’ BLOG
https://creators.bengo4.com/

■ X(Twitter):弁護士ドットコム CREATORS
https://x.com/bengo4_creators

Avatar for 弁護士ドットコム

弁護士ドットコム

June 12, 2026

More Decks by 弁護士ドットコム

Other Decks in Technology

Transcript

  1. Bengo4.com, Inc. 4 Zod などで記述? const schema = z.object({ name:

    z.string().min(1).max(255), parentId: z.string().uuid().optional(), })
  2. Bengo4.com, Inc. 5 同じルールがバックエンドにもある ... type Request struct { Name

    string `json:"name" validate:"required,min=1,max=255"` ParentID *string `json:"parentId" validate:"omitempty,uuid"` } Go の例
  3. Bengo4.com, Inc. 11 .proto のバリデーション記述例 message CreateItemRequest { string name

    = 1 [(buf.validate.field).string = { min_len: 1 // 1文字以上 max_len: 255 // 255文字以下 pattern: "[^\\s\\p{Zs}]" // 空白ではない文字を最低 1文字含む }]; optional string parent_id = 3 [(buf.validate.field).string.uuid = true]; // UUID 形式 }
  4. Bengo4.com, Inc. 12 protovalidate-es TypeScript で proto で定義したバリデーションルー ルを Standard

    Schema として扱うことができる (言語ごとに protovalidate-go など専用ライブラリ が用意されている)
  5. Bengo4.com, Inc. 13 Standard Schema バリデーションライブラリ を同じ形式で扱うための 共通インターフェース type StandardSchema

    = { "~standard": { version: 1 // バージョン vendor: string // 提供元ライブラリ名 validate: (input: unknown) => | { value: unknown } // OK: 検証済みの値 | { issues: { message: string // NG理由 path?: unknown[] // NG箇所 }[] } } }
  6. Bengo4.com, Inc. 14 protovalidate-es でバリデーションする例 import { createStandardSchema } from

    "@/utils/protovalidate" import { InviteUserRequestSchema } from "@/gen/pb/invite_pb" const schema = createStandardSchema(InviteUserRequestSchema) const result = schema["~standard"].validate(formValue)
  7. Bengo4.com, Inc. 15 フォームバリデーションと組み合わせる const schema = createStandardSchema(InviteUserRequestSchema) const r$

    = useRegleSchema(state, schema) const { valid } = await r$.$validate() さらに、Standard Schema 対応のフォームバリデー ションライブラリ(Regle, React Hook Form など)と 接続することができる 下記は Regle との接続例
  8. Bengo4.com, Inc. 18 難しい点1: proto と state の構造差異 protovalidate は

    proto の構造(リクエストの payload の構造)に合わせてバリデーションを検証 するが、フォーム state がこの構造と一致していると は限らない
  9. Bengo4.com, Inc. 19 // state const state = { displayName:

    "契約書", } message UpdateItemRequest { Item item = 1; } message Item { Profile profile = 1; } message Profile { string display_name = 1; } displayName フォーム state proto item.profile.displayName 対応づけ が必要
  10. Bengo4.com, Inc. 21 useProtoRegleSchema(state, UpdateItemRequestSchema, { // フォームの state を

    proto が期待するネスト構造に変換する stateToProtoInit: state => ({ item: { profile: { displayName: state.displayName, }, }, }), // proto のエラー path を フォームの field に戻すための対応表 fieldToProtoPath: proto => ({ displayName: proto.item.profile.displayName, }), })
  11. Bengo4.com, Inc. 23 難しい点2: proto で表現しきれないルール 例: email が間違えていないか再入力させるケース •

    proto は email 形式かをバリデーションできる • 再度、確認用 email を入力させて、入力が一致してるか バリデーションすることは API の契約外なので proto で 定義できない
  12. Bengo4.com, Inc. 24 message InviteUserRequest { string email = 1

    [(buf.validate.field).string.email = true]; // email 形式 } const state = { email: "[email protected]", emailConfirm: "[email protected]", }
  13. Bengo4.com, Inc. 25 解決法: Schema を合成する proto の Standard Schema

    に UI 都合の Standard Schema を合成する仕組みを導入した
  14. Bengo4.com, Inc. 26 const additionalSchema = z .object({ email: z.string(),

    emailConfirm: z.string(), }) .refine(state => state.email === state.emailConfirm, { path: ["emailConfirm"], message: "メールアドレスが一致しません ", }) useProtoRegleSchema(state, InviteUserRequestSchema, { // 内部で InviteUserRequestSchema で合成される additionalSchema, })
  15. Bengo4.com, Inc. 29 解決策: ruleId で変換 • ruleId(protovalidate-es が返すエラー種別) ごとにユーザー向け文

    言を定義し変換 • プロダクトで使用中の ruleId に対して変換漏れがないかリンターで チェック const violationTranslations = { "string.uuid": () => "有効なUUIDを入力してください ", "string.max_len": () => "文字数の上限を超えています ", "required": () => "必須項目です ", }
  16. Bengo4.com, Inc. 30 所感 • proto にバリデーションルールが SSoT として集約されるようになり、堅牢に なった

    • 難しい点を解決するための設計をするのにかなり時間がかかった ◦ が、最近はモデルの性能が高いから楽かも • 難しい点を解決するための API の仕様が複雑になり、人間の理解が辛い ◦ 一方で AI がコードを書き、解説までしてくれる時代なので、あまり気にしなくていいはず