FastAPIでPydanticを活用したデータ検証

プログラミング

FastAPIとPydanticによるデータ検証:網羅的な解説

FastAPIは、Pythonの型ヒントを最大限に活用し、高パフォーマンスでモダンなWeb APIを構築するためのフレームワークです。その強力なデータ検証機能は、Pydanticライブラリとの連携によって実現されています。Pydanticは、Pythonの型ヒントに基づいてデータのバリデーションと設定管理を行います。FastAPIは、Pydanticモデルをリクエストボディ、パスパラメータ、クエリパラメータ、ヘッダーなどに適用することで、APIエンドポイントへの入力データの整合性を自動的に保証します。

Pydanticモデルの基本

Pydanticモデルは、Pythonのクラスとして定義され、BaseModelを継承します。クラスの属性として、期待されるデータの型をPythonの型ヒントで指定します。

基本的なモデル定義

“`python
from pydantic import BaseModel

class Item(BaseModel):
name: str
description: str | None = None
price: float
is_offer: bool = False
“`

この例では、`Item`というPydanticモデルを定義しています。`name`は文字列、`price`は浮動小数点数、`is_offer`はブール値が必須であることを示しています。`description`はオプショナルであり、指定されない場合はNoneになります。

デフォルト値の設定

モデルの属性にデフォルト値を設定することで、クライアントがそのフィールドを省略した場合でも、APIは正常に動作します。

“`python
from pydantic import BaseModel

class User(BaseModel):
username: str
email: str
age: int = 18 # デフォルト値は18
“`

FastAPIでのPydanticモデルの活用

FastAPIでは、関数パラメータにPydanticモデルを指定することで、そのモデルを使用したデータ検証が自動的に行われます。

リクエストボディでの利用

APIエンドポイントでJSON形式のリクエストボディを受け取る場合、関数パラメータにPydanticモデルを指定します。FastAPIは、リクエストボディのJSONをPydanticモデルにパースし、検証を行います。

“`python
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
price: float

@app.post(“/items/”)
async def create_item(item: Item):
return item
“`

このエンドポイントにJSONリクエスト `{ “name”: “Foo”, “price”: 10.5 }` が送信されると、`item`パラメータとして`Item`モデルのインスタンスが渡されます。もしJSONが不正な形式であったり、必須フィールドが欠けていたりすると、FastAPIは自動的に422 Unprocessable Entityエラーを返します。

パスパラメータおよびクエリパラメータでの利用

Pydanticモデルは、パスパラメータやクエリパラメータの型ヒントとしても利用できます。この場合、Pydanticは文字列を期待される型に変換(バリデーション)します。

“`python
from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()

class SearchParams(BaseModel):
query: str
skip: int = 0
limit: int = 10

@app.get(“/search/”)
async def search_items(params: SearchParams):
return {“query”: params.query, “skip”: params.skip, “limit”: params.limit}
“`

この例では、`SearchParams`モデルがクエリパラメータとして使用されます。URLは ` /search/?query=apple&skip=5&limit=20` のようになります。FastAPIは、`query`を文字列、`skip`と`limit`を整数として検証し、必要に応じて型変換を行います。

リクエストヘッダーでの利用

HTTPヘッダーの値もPydanticモデルで検証できます。`Header`関数を使用します。

“`python
from fastapi import FastAPI, Header
from pydantic import BaseModel, Field

app = FastAPI()

class CustomHeader(BaseModel):
x_request_id: str | None = None

@app.get(“/header_test/”)
async def test_header(x_request_id: str = Header(None)):
return {“x-request-id”: x_request_id}
“`

この場合、`x_request_id`ヘッダーが指定されているかどうか、またはその型が正しいかが検証されます。

高度なPydantic機能とFastAPIでの応用

Pydanticは、基本的な型検証に加えて、より複雑なシナリオに対応するための豊富な機能を提供します。

バリデータ

Field関数やvalidatorデコレータを使用すると、より詳細なデータ検証ルールを定義できます。

Fieldによる追加制約

`Field`を使用すると、文字列の長さ、数値の範囲、正規表現マッチングなどを指定できます。

“`python
from pydantic import BaseModel, Field

class Product(BaseModel):
name: str = Field(…, min_length=3, max_length=50) # … は必須フィールドを示す
price: float = Field(…, gt=0) # gt は greater than (より大きい) を意味する
sku: str = Field(…, pattern=”^[A-Z]{3}-d{5}$”) # SKUのフォーマット指定
“`

validatorデコレータ

カスタムバリデーションロジックを実装したい場合に便利です。

“`python
from pydantic import BaseModel, validator, Field

class Event(BaseModel):
name: str
start_date: datetime
end_date: datetime

@validator(‘end_date’)
def end_date_must_be_after_start_date(cls, v, values):
if ‘start_date’ in values and v <= values['start_date']:
raise ValueError('end_date must be after start_date')
return v
“`

この例では、`end_date`が`start_date`よりも後であることを検証しています。

Configクラスによる設定

Pydanticモデルの動作をカスタマイズするために、内部にConfigクラスを定義できます。

“`python
from pydantic import BaseModel, Field

class Settings(BaseModel):
api_key: str
debug_mode: bool = False

class Config:
orm_mode = True # ORMオブジェクトからのマッピングを有効にする
“`

`orm_mode`をTrueに設定すると、SQLAlchemyのようなORMモデルからPydanticモデルへのデータマッピングが容易になります。

aliasによるフィールド名のマッピング

クライアントが送信するJSONのフィールド名と、Pydanticモデルで定義するフィールド名が異なる場合、aliasを使用できます。

“`python
from pydantic import BaseModel, Field

class UserInput(BaseModel):
user_identifier: str = Field(alias=’userId’)
user_name: str = Field(alias=’userName’)
“`

これにより、クライアントは `{“userId”: “abc”, “userName”: “Alice”}` というJSONを送信しても、モデル内では `user_identifier` および `user_name` としてアクセスできます。

OptionalとUnion

Optionalは、フィールドがNoneまたは指定された型であることを示します。Unionは、フィールドが複数の型のうちのいずれかであることを示します。

“`python
from typing import Optional, Union
from pydantic import BaseModel

class Message(BaseModel):
content: Union[str, int]
sender: Optional[str] = None
“`

### エラーハンドリングとデバッグ

FastAPIはPydanticによる検証エラーを自動的にキャッチし、標準化されたエラーレスポンス(通常はJSON形式)を返します。これにより、APIの利用者はエラーの原因を容易に理解できます。

エラーレスポンスの例

“`json
{
“detail”: [
{
“loc”: [
“body”,
“price”
],
“msg”: “value is not a valid float”,
“type”: “type_error.float”
}
]
}
“`

このエラーレスポンスは、リクエストボディの`price`フィールドが浮動小数点数として有効でないことを示しています。locフィールドは、エラーが発生した場所(この場合はボディの`price`)を特定するのに役立ちます。

デバッグモード

開発中は、FastAPIのデバッグモードを有効にすると、より詳細なエラー情報が表示され、問題の特定が容易になります。

まとめ

PydanticはFastAPIのデータ検証の中核を担っており、Pythonの型ヒントを介して、安全で堅牢なAPIを構築するための強力な基盤を提供します。基本的な型指定から、複雑なカスタムバリデーション、フィールドエイリアス、設定管理まで、Pydanticの機能は多岐にわたります。FastAPIはこれらのPydanticの機能をシームレスに統合し、開発者はコードの可読性と保守性を向上させながら、効率的にAPIを開発できます。Pydanticモデルを適切に設計・活用することは、APIの品質と信頼性を高める上で不可欠です。