FastAPI入門ガイド - モダンPython Webフレームワーク基本編
FastAPI入門ガイド - モダンPython Webフレームワーク基本編
FastAPIは、高速でモダンなPython Webフレームワークです。型ヒントを活用した自動バリデーション、自動ドキュメント生成、高いパフォーマンスが特徴で、REST APIの構築に最適です。このガイドでは、FastAPIの基本を実践的なコード例とともに解説します。
📋 目次
- FastAPIとは
- セットアップと環境構築
- 最初のエンドポイント作成
- パスパラメータとクエリパラメータ
- リクエストボディとPydanticモデル
- レスポンスモデル
- HTTPメソッド(GET, POST, PUT, DELETE)
- ステータスコード
- バリデーション基礎
- 自動ドキュメント生成
1. FastAPIとは
FastAPIは、Starlette(高性能ASGIフレームワーク)とPydantic(データバリデーションライブラリ)をベースに構築された、モダンで高速なPython Webフレームワークです。
主な特徴
- ✅ 高速 - NodeJSやGoに匹敵する高いパフォーマンス
- ✅ 型安全 - Python型ヒントによる自動バリデーション
- ✅ 自動ドキュメント - Swagger UI & ReDocが自動生成
- ✅ 開発効率 - 直感的なAPIで高速な開発が可能
- ✅ 非同期対応 - async/awaitをネイティブサポート
- ✅ 標準準拠 - OpenAPI、JSON Schemaに準拠
他フレームワークとの比較
パフォーマンス(リクエスト/秒):
FastAPI: 20,000 - 30,000 req/s
Flask: 2,000 - 5,000 req/s
Django: 1,000 - 3,000 req/s
Node/Express: 15,000 - 25,000 req/s
開発速度:
FastAPI: ⭐⭐⭐⭐⭐ (型ヒント + 自動ドキュメント)
Flask: ⭐⭐⭐⭐ (シンプル)
Django: ⭐⭐⭐ (フル機能だが複雑)
2. セットアップと環境構築
仮想環境の作成
# プロジェクトディレクトリを作成
mkdir fastapi-project
cd fastapi-project
# 仮想環境を作成
python -m venv venv
# 仮想環境を有効化
# Windows
venv\Scripts\activate
# macOS/Linux
source venv/bin/activate
FastAPIのインストール
# FastAPIとUvicorn(ASGIサーバー)をインストール
pip install "fastapi[all]"
# または最小構成
pip install fastapi uvicorn[standard]
# 開発用の依存関係
pip install pytest httpx black flake8
requirements.txt
fastapi==0.104.1
uvicorn[standard]==0.24.0
pydantic==2.5.0
python-multipart==0.0.6 # フォームデータ用
3. 最初のエンドポイント作成
Hello World API
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
@app.get("/hello/{name}")
async def say_hello(name: str):
return {"message": f"Hello {name}"}
サーバーの起動
# 開発サーバーを起動(ホットリロード有効)
uvicorn main:app --reload
# カスタムポート指定
uvicorn main:app --reload --port 8080
# 外部からアクセス可能にする
uvicorn main:app --host 0.0.0.0 --port 8000
自動ドキュメントにアクセス
ブラウザで以下にアクセス:
- Swagger UI:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc - OpenAPI JSON:
http://localhost:8000/openapi.json
4. パスパラメータとクエリパラメータ
パスパラメータ
URLの一部として値を受け取ります。
from fastapi import FastAPI
from enum import Enum
app = FastAPI()
# 基本的なパスパラメータ
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}
# 型ヒントによる自動変換とバリデーション
@app.get("/users/{user_id}")
async def read_user(user_id: int):
# user_idは自動的にintに変換される
# 変換できない値が来るとエラーレスポンス
return {"user_id": user_id, "type": type(user_id).__name__}
# Enumを使った制限
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name == ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
if model_name == ModelName.lenet:
return {"model_name": model_name, "message": "LeCNN all the images"}
return {"model_name": model_name, "message": "Have some residuals"}
# パスパラメータとして任意のパスを受け取る
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}
# /files/home/user/document.txt → file_path = "home/user/document.txt"
クエリパラメータ
URLの?以降のkey=value形式のパラメータ。
from fastapi import FastAPI, Query
from typing import Optional
app = FastAPI()
# 基本的なクエリパラメータ
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
# GET /items/?skip=0&limit=10
# オプショナルなパラメータ
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: Optional[str] = None):
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}
# Queryでバリデーション追加
@app.get("/items/")
async def read_items(
q: Optional[str] = Query(
None,
min_length=3,
max_length=50,
regex="^[a-zA-Z0-9]+$",
description="Search query"
)
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
# 必須クエリパラメータ
@app.get("/items/")
async def read_items(q: str = Query(..., min_length=3)):
# ... は必須パラメータを意味する
return {"q": q}
# 複数の値を受け取る
@app.get("/items/")
async def read_items(q: list[str] = Query([])):
return {"q": q}
# GET /items/?q=foo&q=bar → {"q": ["foo", "bar"]}
5. リクエストボディとPydanticモデル
Pydanticモデルを使って、リクエストボディのバリデーションと型安全性を実現します。
基本的なPydanticモデル
from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
from typing import Optional
from datetime import datetime
app = FastAPI()
# Pydanticモデルの定義
class User(BaseModel):
username: str = Field(..., min_length=3, max_length=50)
email: EmailStr
full_name: Optional[str] = None
age: Optional[int] = Field(None, ge=0, le=150)
is_active: bool = True
@app.post("/users/")
async def create_user(user: User):
return user
# リクエスト例
# {
# "username": "johndoe",
# "email": "john@example.com",
# "full_name": "John Doe",
# "age": 30
# }
ネストしたモデル
from pydantic import BaseModel
from typing import List, Optional
class Image(BaseModel):
url: str
name: str
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float = Field(..., gt=0, description="価格は0より大きい必要があります")
tax: Optional[float] = None
tags: List[str] = []
images: Optional[List[Image]] = None
@app.post("/items/")
async def create_item(item: Item):
return item
# リクエスト例
# {
# "name": "Laptop",
# "price": 1200.00,
# "tax": 120.00,
# "tags": ["electronics", "computers"],
# "images": [
# {"url": "http://example.com/img1.jpg", "name": "Front"},
# {"url": "http://example.com/img2.jpg", "name": "Back"}
# ]
# }
モデルの設定とバリデーター
from pydantic import BaseModel, Field, validator, field_validator
from datetime import datetime
class BlogPost(BaseModel):
title: str = Field(..., min_length=5, max_length=100)
content: str
published_at: Optional[datetime] = None
tags: List[str] = []
# カスタムバリデーター
@field_validator('title')
@classmethod
def title_must_not_contain_spam(cls, v):
if 'spam' in v.lower():
raise ValueError('タイトルにspamを含めることはできません')
return v
@field_validator('tags')
@classmethod
def tags_must_be_unique(cls, v):
if len(v) != len(set(v)):
raise ValueError('タグは重複できません')
return v
# モデル設定
class Config:
json_schema_extra = {
"example": {
"title": "FastAPI入門",
"content": "FastAPIの基本的な使い方...",
"published_at": "2025-11-03T10:00:00",
"tags": ["python", "fastapi", "tutorial"]
}
}
@app.post("/posts/")
async def create_post(post: BlogPost):
return post
6. レスポンスモデル
レスポンスの形式を定義し、自動的にシリアライズします。
from pydantic import BaseModel, EmailStr
from typing import Optional
# レスポンスモデル(パスワードフィールドを除外)
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: Optional[str] = None
class UserOut(BaseModel):
username: str
email: EmailStr
full_name: Optional[str] = None
@app.post("/users/", response_model=UserOut)
async def create_user(user: UserIn):
# パスワードは自動的にレスポンスから除外される
return user
# レスポンス例
# {
# "username": "johndoe",
# "email": "john@example.com",
# "full_name": "John Doe"
# }
# ※ password フィールドは含まれない
レスポンスモデルのオプション
from typing import List
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
# リストで返す
@app.get("/items/", response_model=List[Item])
async def read_items():
return [
{"name": "Item 1", "price": 10.5},
{"name": "Item 2", "price": 20.0, "tax": 2.0}
]
# Noneを除外してレスポンス
@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: int):
return {"name": "Item", "price": 10.5}
# description と tax は設定されていないので除外される
# 特定のフィールドを除外
@app.get("/items/{item_id}", response_model=Item, response_model_exclude={"tax"})
async def read_item(item_id: int):
return {"name": "Item", "price": 10.5, "tax": 1.0}
# tax フィールドは除外される
7. HTTPメソッド(GET, POST, PUT, DELETE)
CRUD操作の実装
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Dict
app = FastAPI()
# データストア(実際はDBを使用)
items_db: Dict[int, dict] = {}
item_id_counter = 1
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
in_stock: bool = True
class ItemUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
price: Optional[float] = None
in_stock: Optional[bool] = None
# CREATE
@app.post("/items/", response_model=Item, status_code=201)
async def create_item(item: Item):
global item_id_counter
item_id = item_id_counter
items_db[item_id] = item.model_dump()
items_db[item_id]["id"] = item_id
item_id_counter += 1
return items_db[item_id]
# READ - すべて取得
@app.get("/items/", response_model=List[Item])
async def read_items(skip: int = 0, limit: int = 10):
items = list(items_db.values())
return items[skip : skip + limit]
# READ - 1件取得
@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: int):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
return items_db[item_id]
# UPDATE - 完全更新
@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: int, item: Item):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
items_db[item_id] = item.model_dump()
items_db[item_id]["id"] = item_id
return items_db[item_id]
# UPDATE - 部分更新
@app.patch("/items/{item_id}", response_model=Item)
async def partial_update_item(item_id: int, item: ItemUpdate):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
stored_item = items_db[item_id]
update_data = item.model_dump(exclude_unset=True)
stored_item.update(update_data)
return stored_item
# DELETE
@app.delete("/items/{item_id}", status_code=204)
async def delete_item(item_id: int):
if item_id not in items_db:
raise HTTPException(status_code=404, detail="Item not found")
del items_db[item_id]
return None # 204 No Content
8. ステータスコード
HTTPステータスコードを適切に使用します。
from fastapi import FastAPI, status, HTTPException
app = FastAPI()
# 明示的にステータスコードを指定
@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(item: Item):
return item
# 成功レスポンス
@app.get("/items/", status_code=status.HTTP_200_OK)
async def read_items():
return [{"name": "Item 1"}]
# 作成成功
@app.post("/users/", status_code=status.HTTP_201_CREATED)
async def create_user(user: User):
return user
# コンテンツなし
@app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_item(item_id: int):
return None
# エラーレスポンス
@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id not in items_db:
# 404 Not Found
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Item not found"
)
return items_db[item_id]
# カスタムエラーレスポンス
@app.post("/users/")
async def create_user(user: User):
if user.username in users_db:
# 409 Conflict
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="Username already exists",
headers={"X-Error": "Username conflict"}
)
return user
# 主要なステータスコード
# 200 OK - 成功
# 201 Created - 作成成功
# 204 No Content - 成功(コンテンツなし)
# 400 Bad Request - リクエストエラー
# 401 Unauthorized - 認証エラー
# 403 Forbidden - 権限エラー
# 404 Not Found - 見つからない
# 409 Conflict - 競合
# 422 Unprocessable Entity - バリデーションエラー(FastAPIのデフォルト)
# 500 Internal Server Error - サーバーエラー
9. バリデーション基礎
Pydanticによる強力なバリデーション機能。
from pydantic import BaseModel, Field, validator, EmailStr, HttpUrl
from typing import Optional, List
from datetime import datetime
class Product(BaseModel):
# 基本的なバリデーション
name: str = Field(..., min_length=1, max_length=100)
sku: str = Field(..., regex=r'^[A-Z]{3}-[0-9]{4}$')
price: float = Field(..., gt=0, le=1000000)
discount: Optional[float] = Field(None, ge=0, le=100)
# 型による自動バリデーション
email: EmailStr
website: HttpUrl
tags: List[str] = Field(default_factory=list, max_items=10)
created_at: datetime = Field(default_factory=datetime.now)
# カスタムバリデーター
@field_validator('discount')
@classmethod
def validate_discount(cls, v, values):
if v and v > 50:
raise ValueError('割引は50%以下にしてください')
return v
@field_validator('tags')
@classmethod
def validate_tags(cls, v):
if len(v) != len(set(v)):
raise ValueError('タグに重複があります')
return [tag.lower() for tag in v]
@app.post("/products/")
async def create_product(product: Product):
return product
# バリデーションエラーのレスポンス例
# {
# "detail": [
# {
# "loc": ["body", "price"],
# "msg": "ensure this value is greater than 0",
# "type": "value_error.number.not_gt"
# }
# ]
# }
クエリパラメータのバリデーション
from fastapi import Query
@app.get("/search/")
async def search_items(
q: str = Query(
...,
min_length=3,
max_length=50,
regex=r'^[a-zA-Z0-9s]+$',
title="検索クエリ",
description="検索するキーワード(3-50文字、英数字とスペースのみ)"
),
page: int = Query(1, ge=1, le=100, description="ページ番号"),
size: int = Query(10, ge=1, le=100, description="1ページあたりの件数")
):
return {
"q": q,
"page": page,
"size": size,
"offset": (page - 1) * size
}
10. 自動ドキュメント生成
FastAPIの最大の魅力の一つは、自動生成されるインタラクティブなAPIドキュメントです。
メタ情報の設定
from fastapi import FastAPI
app = FastAPI(
title="My Awesome API",
description="""
# FastAPI入門ガイド
このAPIは以下の機能を提供します:
## Items
アイテムの**CRUD操作**を実行できます。
## Users
ユーザー管理機能を提供します。
## 特徴
* 自動バリデーション
* 型安全
* 高速なレスポンス
""",
version="1.0.0",
terms_of_service="http://example.com/terms/",
contact={
"name": "API Support",
"url": "http://example.com/contact/",
"email": "support@example.com",
},
license_info={
"name": "MIT License",
"url": "https://opensource.org/licenses/MIT",
},
)
エンドポイントのドキュメント
from fastapi import FastAPI, Path, Query, Body
@app.post(
"/items/",
response_model=Item,
status_code=201,
summary="アイテムを作成",
description="新しいアイテムをデータベースに作成します。",
response_description="作成されたアイテム",
tags=["items"],
)
async def create_item(
item: Item = Body(
...,
example={
"name": "Laptop",
"description": "高性能ノートPC",
"price": 1200.00,
"in_stock": True
}
)
):
"""
アイテムを作成します:
- **name**: アイテム名(必須)
- **description**: 説明(オプション)
- **price**: 価格(必須、0より大きい値)
- **in_stock**: 在庫状況(デフォルト: true)
"""
return item
タグによるグループ化
tags_metadata = [
{
"name": "items",
"description": "アイテムの管理操作",
},
{
"name": "users",
"description": "ユーザー管理操作",
"externalDocs": {
"description": "ユーザー管理の詳細ドキュメント",
"url": "https://example.com/docs/users",
},
},
]
app = FastAPI(openapi_tags=tags_metadata)
@app.get("/items/", tags=["items"])
async def read_items():
return [{"name": "Item 1"}]
@app.get("/users/", tags=["users"])
async def read_users():
return [{"username": "user1"}]
📝 まとめ
FastAPI基本編で学んだ内容:
基礎知識
- ✅ FastAPIの特徴 - 高速、型安全、自動ドキュメント
- ✅ セットアップ - 仮想環境、インストール、起動
- ✅ エンドポイント作成 - ルーティング、デコレーター
データ処理
- ✅ パスパラメータ - URLからの値取得、型変換
- ✅ クエリパラメータ - オプショナル、必須、バリデーション
- ✅ リクエストボディ - Pydanticモデル、ネストしたデータ
レスポンス
- ✅ レスポンスモデル - 型安全なレスポンス、フィールド除外
- ✅ HTTPメソッド - GET, POST, PUT, PATCH, DELETE
- ✅ ステータスコード - 適切なコード選択
品質保証
- ✅ バリデーション - 自動バリデーション、カスタムルール
- ✅ ドキュメント - Swagger UI、ReDoc、メタ情報
🔗 次のステップ
基本をマスターしたら、次の記事で以下を学習しましょう:
- FastAPI中級編 - 認証・認可、ミドルウェア、CORS
- FastAPI実践編 - データベース連携、テスト、デプロイ
🔗 参考リンク
FastAPIで高速で型安全なWeb APIを構築しましょう!
著者について
kitahara-devによって執筆されました。