当サイトは、アフィリエイト広告を利用しています

【FastAPI】バリデーションを理解するField・Annotated・Validator の使い分け

作成日:2026月06月21日
更新日:2026年06月21日

FastAPIでAPIを実装する場合、リクエストの入力値はPydanticのモデルで検証することが多い。

たとえば、次のようなリクエストモデルを考える。

基本的なPydanticモデル
from pydantic import BaseModel
class Item(BaseModel):
name: str
quantity: int

このモデルでは、namestrquantityintとして検証される。

しかし、実務では型だけでは足りないことが多い。

たとえば、次のようなルールを付けたい場合がある。

  • nameは1文字以上20文字以下
  • quantityは1以上
  • 前後の空白を除去したい
  • 空文字は禁止したい
  • passwordpassword_confirmが一致しているか確認したい

このような入力検証を実装するために、Pydanticには複数の仕組みが用意されている。

代表的なものは次のとおりである。

  • Field
  • Annotated
  • BeforeValidator
  • AfterValidator
  • WrapValidator
  • PlainValidator
  • @field_validator
  • @model_validator

この記事では、それぞれがどのようなものか、何が違うのか、実務ではどう使い分けるかを整理する。

全体像

Pydantic / FastAPIの入力検証は、次のように整理できる。

全体像
Pydantic / FastAPIの入力検証
├─ Field
│ └─ フィールドに制約やメタデータを付ける
│ └─ 文字数、数値範囲、説明、デフォルト値など
├─ Annotated
│ └─ 型にメタデータを付ける
│ ├─ Fieldを型に結びつける
│ └─ Validatorを型に結びつける
├─ AnnotatedパターンのValidator
│ ├─ BeforeValidator
│ │ └─ Pydanticの型検証前に動く
│ │ └─ 入力値の正規化に使う
│ │
│ ├─ AfterValidator
│ │ └─ Pydanticの型検証後に動く
│ │ └─ 型確定後の追加チェックに使う
│ │
│ ├─ WrapValidator
│ │ └─ Pydanticの検証処理を包む
│ │ └─ 前処理と後処理をまとめて制御する
│ │
│ └─ PlainValidator
│ └─ Pydanticの標準検証を置き換える
│ └─ 独自の変換・検証に使う
└─ デコレータパターンのValidator
├─ @field_validator
│ └─ モデル内のフィールドを検証する
└─ @model_validator
└─ モデル全体を検証する
└─ 複数フィールドの関係チェックなどに使う

Validatorの2つのパターン

PydanticのフィールドValidatorには、次の2つの定義方法がある。

Validatorの定義方法
Annotatedパターン
├─ BeforeValidator
├─ AfterValidator
├─ WrapValidator
└─ PlainValidator
デコレータパターン
├─ @field_validator(mode="before")
├─ @field_validator(mode="after")
├─ @field_validator(mode="wrap")
└─ @field_validator(mode="plain")

それぞれは、次のように対応している。

Validatorの対応関係
BeforeValidator
↔ @field_validator(mode="before")
AfterValidator
↔ @field_validator(mode="after")
WrapValidator
↔ @field_validator(mode="wrap")
PlainValidator
↔ @field_validator(mode="plain")

どちらのパターンでも、Pydantic本体の検証に対して、処理を実行するタイミングを指定できる。

違いは、Validatorをどこに定義するかである。

定義場所の違い
型にValidatorを結びつける
→ Annotatedパターン
モデルクラス内のフィールドにValidatorを定義する
→ デコレータパターン

この記事では、型として切り出して再利用しやすいAnnotatedパターンと、
モデルクラス内に定義するデコレータパターンに分けて説明する。

Fieldとは何か

Fieldは、Pydanticのフィールドに制約やメタデータを付けるための仕組みである。

たとえば、文字列の長さや数値範囲を指定できる。

Fieldの基本例
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(min_length=1, max_length=20)
age: int = Field(ge=0, le=120)

このモデルでは、次の制約が付く。

  • name

    • 1文字以上20文字以下
  • age

    • 0以上120以下

Fieldは、次のような宣言的に書ける制約に向いている。

  • 文字数
  • 数値範囲
  • 正規表現
  • デフォルト値
  • 説明文
  • examples

たとえば、次のようにフィールドの説明も付けられる。

Fieldで説明を付ける
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(
min_length=1,
max_length=20,
description="ユーザー名",
)

Fieldは、単純な制約やJSON Schema用のメタデータを書く場所である。

Annotatedとは何か

Annotatedは、Pythonの型ヒントにメタデータを付与するための仕組みである。

基本形は次のようになる。

Annotatedの基本形
Annotated[元の型, メタデータ]

Pydanticでは、FieldやValidatorを型に結びつけるために使える。

AnnotatedとFieldの例
from typing import Annotated
from pydantic import Field
UserName = Annotated[
str,
Field(min_length=1, max_length=20),
]

これは、次のように読める。

構文
UserName
├─ 型: str
└─ メタデータ: Field(min_length=1, max_length=20)
  • Annotated自体が検証しているわけではない
  • Annotatedは、型にメタデータを付けるための箱である

このUserNameは、Pydanticモデルで使える。

Annotatedで定義した型を使う
from pydantic import BaseModel
class UserCreateRequest(BaseModel):
name: UserName

このようにすると、namestrであり、かつ1文字以上20文字以下という制約を持つ。

Annotatedについては、次の記事でも詳しくまとめている。

FieldAnnotatedの違い

Fieldは、制約やメタデータそのものである。
Annotatedは、それらを型に結びつけるための仕組みである。

次の2つは、どちらもnameに文字数制限を付けている。

Fieldを直接書く例
from pydantic import BaseModel, Field
class UserCreateRequest(BaseModel):
name: str = Field(min_length=1, max_length=20)
Annotatedで型として切り出す例
from typing import Annotated
from pydantic import BaseModel, Field
UserName = Annotated[
str,
Field(min_length=1, max_length=20),
]
class UserCreateRequest(BaseModel):
name: UserName

違いは、再利用しやすさである。

Fieldをモデル内に直接書く場合、制約はそのフィールドに閉じる。

Fieldを直接書く場合
class UserCreateRequest(BaseModel):
name: str = Field(min_length=1, max_length=20)
class CompanyCreateRequest(BaseModel):
contact_name: str = Field(min_length=1, max_length=20)

一方、Annotatedで型として切り出すと、同じ制約を複数箇所で使い回せる。

Annotatedで再利用する場合
UserName = Annotated[
str,
Field(min_length=1, max_length=20),
]
class UserCreateRequest(BaseModel):
name: UserName
class CompanyCreateRequest(BaseModel):
contact_name: UserName

判断基準は次のようになる。

FieldとAnnotatedの使い分け
そのフィールドだけで使う制約
→ Fieldをモデル内に直接書く
複数箇所で使い回したい制約
→ Annotatedで型として切り出す

ただし、Annotatedは再利用する場合にしか使えないわけではない。
再利用はAnnotatedを使う大きなメリットの1つである。

AnnotatedパターンのValidator

Annotatedには、FieldだけでなくValidatorも付与できる。

代表的なものは次の4つである。

  • BeforeValidator
  • AfterValidator
  • WrapValidator
  • PlainValidator

これらは、Pydanticの検証フローのどこで処理するかが異なる。

検証フロー
入力値
BeforeValidator
Pydantic本体の型検証・型変換
AfterValidator
最終値

WrapValidatorは、Pydantic本体の検証処理を包み込む。
PlainValidatorは、Pydantic本体の型検証を行わず、独自の関数だけで変換・検証を完結させる。

BeforeValidatorとは?

BeforeValidatorは、Pydanticが型変換・型検証を行う前に実行される。

主な用途は、入力値の正規化である。
たとえば、文字列の前後の空白を除去したい場合に使える。

BeforeValidatorの例
from typing import Any, Annotated
from pydantic import BaseModel, BeforeValidator
def trim(v: Any) -> Any:
if isinstance(v, str):
return v.strip()
return v
TrimmedStr = Annotated[
str,
BeforeValidator(trim),
]
class UserCreateRequest(BaseModel):
name: TrimmedStr

まず、trim関数を定義する。

次に、BeforeValidator(trim)Annotatedに指定する。

この場合、次の入力を受け取るとする。

入力例
{
"name": " taro "
}

処理の流れは次のようになる。

処理の流れ
入力値 " taro "
BeforeValidator(trim)
"taro"
Pydanticのstr検証
最終値 "taro"

BeforeValidatorは型検証前に動く。

そのため、入力値がまだstrであるとは限らない。

次のように書くと、入力値によってはエラーになる可能性がある。

入力型をstrに限定した例
def trim(v: str) -> str:
return v.strip()

入力値がNoneや数値だった場合、strip()を呼び出せないためである。

安全に処理する場合は、Anyで受け取り、必要に応じて型を確認する。

入力型を確認する例
from typing import Any
def trim(v: Any) -> Any:
if isinstance(v, str):
return v.strip()
return v

BeforeValidatorは、次のような型検証前に値を加工する処理に向いている。

  • 前後の空白を除去する
  • 空文字をNoneに変換する
  • 全角数字を半角数字に変換する
  • カンマ区切り文字列をlistに変換する
  • 単一値をlistに変換する
  • 入力表記の揺れを吸収する

AfterValidatorとは?

AfterValidatorは、Pydanticが型変換・型検証を行った後に実行される。

Validator関数に渡される時点では、基本的に指定した型として扱える。

たとえば、正の整数だけを許可する型を定義する。

AfterValidatorの例
from typing import Annotated
from pydantic import BaseModel, AfterValidator
def must_be_positive(v: int) -> int:
if v <= 0:
raise ValueError("正の整数である必要があります")
return v
PositiveInt = Annotated[
int,
AfterValidator(must_be_positive),
]
class Item(BaseModel):
quantity: PositiveInt

まず、正の整数であることを確認する関数を定義する。

次に、AfterValidatorとしてAnnotatedに指定する。

処理の流れは次のようになる。

処理の流れ
入力値
Pydanticのint検証・変換
AfterValidator(must_be_positive)
最終値

AfterValidatorの時点では、vintとして扱える。

そのため、BeforeValidatorよりも型安全に書きやすい。

AfterValidatorは、次のような処理に向いている。

  • 正数チェック
  • 偶数・奇数チェック
  • 文字列フォーマットチェック
  • 日付の妥当性チェック
  • Enum変換後の追加チェック
  • 単一値に閉じた業務ルールの検証

入力値を整える場合はBeforeValidatorを使う。

型が決まった後に追加チェックする場合はAfterValidatorを使う。

BeforeValidatorAfterValidatorを組み合わせる

BeforeValidatorAfterValidatorは組み合わせて使える。

たとえば、文字列について次のルールを作りたいとする。

  • 前後の空白を除去する
  • 空文字は禁止する

この場合、次のように定義できる。

BeforeValidatorとAfterValidatorの組み合わせ
from typing import Any, Annotated
from pydantic import BaseModel, BeforeValidator, AfterValidator
def trim(v: Any) -> Any:
if isinstance(v, str):
return v.strip()
return v
def not_empty(v: str) -> str:
if v == "":
raise ValueError("空文字は不可")
return v
NonEmptyStr = Annotated[
str,
BeforeValidator(trim),
AfterValidator(not_empty),
]
class UserCreateRequest(BaseModel):
name: NonEmptyStr

まず、BeforeValidator(trim)で前後の空白を除去する。

次に、Pydanticがstrとして検証する。

最後に、AfterValidator(not_empty)で空文字を禁止する。

処理の流れは次のようになる。

処理の流れ
入力値 " taro "
BeforeValidator(trim)
"taro"
Pydanticのstr検証
AfterValidator(not_empty)
最終値 "taro"

このNonEmptyStrは、通常のフィールドとして使える。

NonEmptyStrを使う
class UserCreateRequest(BaseModel):
name: NonEmptyStr

また、listの要素にも使える。

listの要素に使う
class TagRequest(BaseModel):
tags: list[NonEmptyStr]

この場合、tagsの各要素に対して、空白除去と空文字チェックが適用される。

WrapValidatorとは?

WrapValidatorは、Pydantic本体の検証処理を包み込むValidatorである。

  • BeforeValidator

    • Pydantic本体の検証前に処理を差し込む
  • AfterValidator

    • Pydantic本体の検証後に処理を差し込む

一方、WrapValidatorはPydanticの検証処理全体を包み込む。

そのため、Pydantic本体の検証前後に処理を追加できる。

概念的には次のような流れになる。

処理の流れ
WrapValidator開始
前処理
handler(v)でPydantic本体の検証
後処理
返却

例を示す。

WrapValidatorの例
from typing import Any, Annotated
from pydantic import BaseModel, ValidatorFunctionWrapHandler, WrapValidator
def wrap_trim(
v: Any,
handler: ValidatorFunctionWrapHandler,
) -> str:
# 前処理
if isinstance(v, str):
v = v.strip()
# Pydantic本体の検証
value = handler(v)
# 後処理
if value == "":
raise ValueError("空文字は不可")
return value
NonEmptyStr = Annotated[
str,
WrapValidator(wrap_trim),
]
class User(BaseModel):
name: NonEmptyStr

処理の流れは次のとおりである。

  • handler(v)を呼び出す前に前処理を行う
  • handler(v)によってPydantic本体の検証を実行する
  • Pydantic検証後の値に対して後処理を行う
  • 最終的な値を返す

WrapValidatorは柔軟だが、その分、処理の流れが分かりにくくなりやすい。

前処理だけならBeforeValidator、後処理だけならAfterValidatorを使う方が読みやすい。

WrapValidatorは、前後処理やエラー処理などを1つの関数で制御したい場合に検討する。

PlainValidatorとは?

PlainValidatorは、Pydanticの標準検証を使わず、自分の関数だけで変換・検証を完結させるValidatorである。

たとえば、独自に整数へ変換する型を作る場合を考える。

PlainValidatorの例
from typing import Any, Annotated
from pydantic import BaseModel, PlainValidator
def parse_int(v: Any) -> int:
if isinstance(v, int):
return v
if isinstance(v, str) and v.isdecimal():
return int(v)
raise ValueError("整数に変換できません")
MyInt = Annotated[
int,
PlainValidator(parse_int),
]
class Item(BaseModel):
count: MyInt

PlainValidatorを使うと、Pydanticの通常の型検証を通さずに、Validator関数の戻り値がそのまま結果になる。

PlainValidatorの処理の流れは次のようになる。

処理の流れ
PlainValidator開始
入力値を受け取る
自分で変換・検証する
Pydantic本体の型検証は行わない
Validator関数の戻り値を最終値として扱う
返却

PlainValidatorは、Pydanticの標準検証を置き換える。

そのため、BeforeValidatorAfterValidatorのように、Pydantic本体の型検証の前後に処理を差し込むものではない。
入力値をどのように解釈し、どの値を返すかをValidator関数側で決める必要がある。
PlainValidatorは強力だが、Pydanticの標準検証を利用しなくなるため、使いどころには注意が必要である。

実務では、次の順番で検討するとよい。

Validatorの選び方
Fieldで表現できるか
BeforeValidator / AfterValidatorで表現できるか
WrapValidatorで制御する必要があるか
PlainValidatorで標準検証を置き換える必要があるか

デコレータパターンのValidator

Pydanticでは、モデルクラス内にデコレータを使ってValidatorを定義することもできる。

代表的なものは次の2つである。

  • @field_validator
  • @model_validator

@field_validatorは、モデル内の特定フィールドに対して変換や検証を行う。
@model_validatorは、モデル全体に対して検証を行う。

特に、複数フィールドの関係を検証する場合に向いている。

@field_validatorとは?

@field_validatorは、データモデルクラスの中にフィールドの検証処理を書くためのデコレータである。

field_validatorの例
from typing import Any
from pydantic import BaseModel, field_validator
class UserCreateRequest(BaseModel):
first_name: str
last_name: str
@field_validator("first_name", "last_name", mode="before")
@classmethod
def trim_name(cls, v: Any) -> Any:
if isinstance(v, str):
return v.strip()
return v

この例では、first_namelast_nameの両方に対して、Pydantic本体の型検証前に空白除去を行っている。

@field_validatorは、次のような場合に向いている。

  • 特定モデルのフィールドだけを検証したい
  • 同じモデル内の複数フィールドに同じ処理を適用したい
  • そのモデル固有の文脈でフィールドを検証したい

同じ検証ルールを複数のモデルで使い回したい場合は、Annotatedパターンの方が向いている。

Annotatedパターンで再利用する例
NonEmptyStr = Annotated[
str,
BeforeValidator(trim),
AfterValidator(not_empty),
]

使い分けは次のようになる。

Annotatedパターンとの使い分け
型として複数箇所で再利用したい
→ Annotatedパターン
特定モデル固有のフィールド検証として定義したい
→ @field_validator

@model_validatorとは?

@model_validatorは、モデル全体を検証するためのデコレータである。

特に、複数フィールドの関係を見る場合に使う。

たとえば、パスワード確認のようなケースを考える。

model_validatorの例
from typing_extensions import Self
from pydantic import BaseModel, model_validator
class UserCreateRequest(BaseModel):
password: str
password_confirm: str
@model_validator(mode="after")
def check_passwords_match(self) -> Self:
if self.password != self.password_confirm:
raise ValueError("パスワードが一致しません")
return self

この検証では、passwordpassword_confirmの両方を確認する必要がある。

このような検証は、単一フィールドの制約では表現しにくい。

モデル全体の整合性チェックとして扱う方が自然である。

@model_validatorは、次のような検証に向いている。

  • start_date <= end_date
  • password == password_confirm
  • emailまたはphoneのどちらか一方は必須
  • statusによって必須項目が変わる
  • 複数フィールドの組み合わせで妥当性を判断する

単一の値だけを検証する場合は、Field、Annotatedパターン、または@field_validatorを使う。
複数フィールドの関係を検証する場合は、@model_validatorを使う。

Validator関数のルール

Annotatedパターンとデコレータパターンのどちらでも、Validator関数を定義する。
Validator関数では、検証後の値を返す必要がある。

たとえば、次のように書く。

値を返すValidator
def not_empty(v: str) -> str:
if v == "":
raise ValueError("空文字は不可")
return v

入力値を不正とする場合は、ValueErrorなどの例外を送出する。
問題がなければ、検証後の値を返す。

次のようにreturnを忘れると、正常な入力でもNoneが返される。

returnを忘れた例
def not_empty(v: str) -> str:
if v == "":
raise ValueError("空文字は不可")

Validatorは、単に値をチェックする関数ではない。
入力値を受け取り、必要に応じて変換・検証し、その結果を返す関数である。

入力検証の使い分けと判断基準

ここまで、Pydanticが提供する次の仕組みについて説明した。

  • Field
  • Annotated
  • BeforeValidator
  • AfterValidator
  • WrapValidator
  • PlainValidator
  • @field_validator
  • @model_validator

実務でこれらを使い分ける場合は、機能名から選ぶのではなく、まず 検証ルールをどこに持たせるか を考えると整理しやすい。

入力検証の判断基準
その検証は特定のデータモデル内だけで使うか
├─ はい
│ ├─ 宣言的な単純な制約か
│ │ └─ Field
│ │
│ ├─ 特定フィールドの変換・検証か
│ │ └─ @field_validator
│ │
│ └─ 複数フィールドの関係を検証するか
│ └─ @model_validator
└─ いいえ
└─ 複数のモデルやフィールドで再利用したい
└─ Annotatedパターンで制約付き型として定義する
├─ 宣言的な制約
│ └─ Field
├─ 型検証前に入力を整える
│ └─ BeforeValidator
├─ 型検証後に追加チェックする
│ └─ AfterValidator
├─ 検証処理の前後をまとめて制御する
│ └─ WrapValidator
└─ Pydanticの標準検証を置き換える
└─ PlainValidator

この図は、絶対的なルールではない。
Annotatedは再利用しない場合でも使えるが、
複数のモデルやフィールドで検証ルールを再利用したい場合に特に向いている。

データモデル内で完結する検証

検証ルールが特定のデータモデルでしか使われない場合は、モデル内に定義する。

  • 文字数や数値範囲などの単純な制約

    • Field
    • 特定モデルのnameにだけ文字数制限を付ける場合は、Fieldをモデル内に直接書けばよい
  • 特定フィールドの変換・検証

    • @field_validator
    • 同じモデル内の特定フィールドだけに空白除去などを適用する場合は、@field_validatorを使う
  • 複数フィールドの関係を検証

    • @model_validator
    • 開始日と終了日の前後関係など、複数フィールドを見なければ判断できない検証には、@model_validatorを使う

モデル固有の検証であれば、無理に外部へ切り出さず、モデル内に書く方が処理の所在を把握しやすい。

複数箇所で再利用する検証

同じ検証ルールを複数のモデルやフィールドで使う場合は、Annotatedを使って型として切り出す。
定義した型は、複数箇所で再利用できる。

  • 文字数や数値範囲などの宣言的な制約

    • Annotated + Field
  • 型検証前の入力正規化

    • Annotated + BeforeValidator
  • 型検証後の追加チェック

    • Annotated + AfterValidator
  • 検証処理全体の制御

    • Annotated + WrapValidator
  • Pydantic標準検証の置き換え

    • Annotated + PlainValidator

基本的には、入力値を整える場合はBeforeValidatorを使い、
型が確定した後に追加チェックする場合はAfterValidatorを使う。

WrapValidatorPlainValidatorは検証フローを大きく制御するため、
BeforeValidatorAfterValidatorでは対応できない場合に検討する。

Annotatedを使う目的は、単に書き方を変えることではない。
検証ルールを型に結びつけ、複数のモデルやフィールドで再利用しやすくすることである。

使い分けのまとめ

重要なのは、次のどちらに該当するかを最初に判断することである。

  • モデル固有のルール
  • 複数箇所で再利用するルール

関連記事

新着記事

タグ一覧
top