はじめに
「PythonでAPIを作ってみたいけど、どこから始めればいいかわからない」
そんな方に向けて、FastAPI を使ってREST APIをゼロから構築する方法を丁寧に解説します。
FastAPIはPython製のWebフレームワークで、近年急速に普及しています。その理由は:
- 高速:Starlette + Pydanticベースで、NodeやGoに匹敵するパフォーマンス
- 型安全:Pythonの型ヒントを活かした自動バリデーション
- 自動ドキュメント生成:コードを書くだけでSwagger UIが自動生成される
- 学習コストが低い:シンプルで直感的なAPI設計
この記事では以下の内容をカバーします:
- 環境構築とインストール
- 最初のAPIを作る(GETリクエスト)
- POSTリクエストとリクエストボディの受け取り
- Pydanticによるバリデーション
- SQLiteとの連携(データの永続化)
- Swagger UIの活用
1. 環境構築とインストール
必要なもの
- Python 3.8以上
- pip(Pythonのパッケージマネージャー)
まずPythonのバージョンを確認しましょう。
python --version
# Python 3.11.x などと表示されればOK
仮想環境の作成(推奨というか必須)
プロジェクトごとにパッケージを管理するために、仮想環境を使います。
# プロジェクトフォルダを作成
mkdir fastapi-tutorial
cd fastapi-tutorial
# 仮想環境を作成
python -m venv venv
# 仮想環境を有効化
# macOS / Linux
source venv/bin/activate
# Windows
venv\Scripts\activate
FastAPIとuvicornのインストール
pip install fastapi uvicorn[standard]
- fastapi:フレームワーク本体
- uvicorn:FastAPIを動かすための高速ASGIサーバー
インストールが完了したら準備OKです!
2. 最初のAPIを作る(GETリクエスト)
Hello World
プロジェクトフォルダに main.py を作成します。
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello, FastAPI!"}
これだけです。たった5行でAPIサーバーが完成します。
サーバーの起動
uvicorn main:app --reload
-
main:main.pyのファイル名 -
app:FastAPIインスタンスの変数名 -
--reload:コード変更時に自動でリロード(開発時に便利)
ターミナルに以下のようなログが出れば成功です。
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process
ブラウザで http://127.0.0.1:8000 を開くと:
{"message": "Hello, FastAPI!"}
が表示されます。🎉
パスパラメータの受け取り
URLの一部を変数として受け取ることができます。
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}
http://127.0.0.1:8000/items/42 にアクセスすると:
{"item_id": 42}
Pythonの型ヒント int を付けるだけで、自動的に整数に変換・バリデーションしてくれます。文字列を渡すと自動でエラーを返してくれます。
クエリパラメータの受け取り
URLの ?key=value 形式のパラメータも簡単に受け取れます。
@app.get("/search")
def search_items(keyword: str, limit: int = 10):
return {"keyword": keyword, "limit": limit}
http://127.0.0.1:8000/search?keyword=python&limit=5 にアクセスすると:
{"keyword": "python", "limit": 5}
limit: int = 10 のようにデフォルト値を設定することもできます。
3. POSTリクエストとリクエストボディ
GETはデータを取得するためのリクエストですが、POSTはデータを送信・作成するときに使います。
シンプルなPOSTエンドポイント
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.post("/items")
def create_item(item: Item):
return {"message": "作成しました", "item": item}
Item クラスがリクエストボディのスキーマを定義しています。Pydanticの BaseModel を継承するだけで、自動的にJSONをPythonオブジェクトに変換してくれます。
curlでテスト
curl -X POST "http://127.0.0.1:8000/items" \
-H "Content-Type: application/json" \
-d '{"name": "りんご", "price": 150.0}'
レスポンス:
{
"message": "作成しました",
"item": {
"name": "りんご",
"price": 150.0
}
}
4. Pydanticによるバリデーション
FastAPIの強力な機能のひとつが、Pydanticを使った自動バリデーションです。
バリデーションの基本
from pydantic import BaseModel, Field
class Item(BaseModel):
name: str = Field(..., min_length=1, max_length=50, description="商品名")
price: float = Field(..., gt=0, description="価格(0より大きい値)")
stock: int = Field(default=0, ge=0, description="在庫数(0以上)")
description: str | None = Field(default=None, description="商品説明(任意)")
Fieldのオプション:
| オプション | 意味 |
|---|---|
... |
必須フィールド |
min_length / max_length
|
文字列の長さ制限 |
gt / ge
|
より大きい / 以上 |
lt / le
|
より小さい / 以下 |
default |
デフォルト値 |
バリデーションエラーの確認
バリデーションに失敗した場合、FastAPIは自動的に 422 Unprocessable Entity を返してくれます。
# priceに負の値を送ってみる
curl -X POST "http://127.0.0.1:8000/items" \
-H "Content-Type: application/json" \
-d '{"name": "りんご", "price": -100}'
レスポンス:
{
"detail": [
{
"type": "greater_than",
"loc": ["body", "price"],
"msg": "Input should be greater than 0",
"input": -100
}
]
}
エラー内容が詳細に返ってくるので、フロントエンドからも扱いやすいです。
レスポンスモデルの定義
レスポンスの形式も型で定義できます。
from pydantic import BaseModel
class ItemResponse(BaseModel):
id: int
name: str
price: float
@app.post("/items", response_model=ItemResponse)
def create_item(item: Item):
# DBに保存する処理(後述)
return {"id": 1, "name": item.name, "price": item.price}
response_model を指定することで、余分なフィールドが自動的に除外され、ドキュメントにも反映されます。
5. SQLiteとの連携(データの永続化)
インメモリのデータはサーバー再起動で消えてしまいます。SQLiteを使ってデータを永続化しましょう。
必要なパッケージのインストール
pip install sqlalchemy
ファイル構成
fastapi-tutorial/
├── main.py
├── database.py # DB接続設定
└── models.py # テーブル定義
database.py(DB接続設定)
# database.py
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
DATABASE_URL = "sqlite:///./items.db"
engine = create_engine(
DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
models.py(テーブル定義)
# models.py
from sqlalchemy import Column, Integer, String, Float
from database import Base
class ItemModel(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False)
price = Column(Float, nullable=False)
stock = Column(Integer, default=0)
main.py(全体を組み合わせる)
# main.py
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from pydantic import BaseModel, Field
from database import engine, get_db, Base
from models import ItemModel
# テーブルを作成
Base.metadata.create_all(bind=engine)
app = FastAPI(title="商品管理API", version="1.0.0")
# Pydanticスキーマ
class ItemCreate(BaseModel):
name: str = Field(..., min_length=1, max_length=50)
price: float = Field(..., gt=0)
stock: int = Field(default=0, ge=0)
class ItemResponse(BaseModel):
id: int
name: str
price: float
stock: int
class Config:
from_attributes = True # SQLAlchemyモデルから変換を許可
# 商品一覧を取得
@app.get("/items", response_model=list[ItemResponse])
def get_items(db: Session = Depends(get_db)):
items = db.query(ItemModel).all()
return items
# 商品を1件取得
@app.get("/items/{item_id}", response_model=ItemResponse)
def get_item(item_id: int, db: Session = Depends(get_db)):
item = db.query(ItemModel).filter(ItemModel.id == item_id).first()
if item is None:
raise HTTPException(status_code=404, detail="商品が見つかりません")
return item
# 商品を作成
@app.post("/items", response_model=ItemResponse, status_code=201)
def create_item(item: ItemCreate, db: Session = Depends(get_db)):
db_item = ItemModel(**item.model_dump())
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item
# 商品を削除
@app.delete("/items/{item_id}", status_code=204)
def delete_item(item_id: int, db: Session = Depends(get_db)):
item = db.query(ItemModel).filter(ItemModel.id == item_id).first()
if item is None:
raise HTTPException(status_code=404, detail="商品が見つかりません")
db.delete(item)
db.commit()
動作確認
# 商品を作成
curl -X POST "http://127.0.0.1:8000/items" \
-H "Content-Type: application/json" \
-d '{"name": "りんご", "price": 150, "stock": 100}'
# 一覧を取得
curl http://127.0.0.1:8000/items
# 1件取得
curl http://127.0.0.1:8000/items/1
# 削除
curl -X DELETE http://127.0.0.1:8000/items/1
6. Swagger UIの活用
FastAPIの最大の特徴のひとつが、コードを書くだけで自動的にAPIドキュメントが生成される点です。
アクセス方法
サーバーを起動した状態で以下のURLにアクセス:
-
Swagger UI:
http://127.0.0.1:8000/docs -
ReDoc:
http://127.0.0.1:8000/redoc
Swagger UIでできること
- 全エンドポイントの一覧表示:GETやPOSTなどHTTPメソッドごとに色分け
- リクエスト・レスポンスの仕様確認:必須パラメータや型が一目でわかる
- ブラウザ上でAPIを直接テスト:「Try it out」ボタンからリクエストを送れる
- 自動的に最新状態を反映:コードを変更すると即座にドキュメントに反映
タイトルや説明を追加する
app = FastAPI(
title="商品管理API",
description="FastAPIで作ったシンプルな商品管理APIです。",
version="1.0.0",
)
各エンドポイントにも説明を追加できます:
@app.get(
"/items",
response_model=list[ItemResponse],
summary="商品一覧を取得",
description="登録されているすべての商品を返します。",
)
def get_items(db: Session = Depends(get_db)):
...
まとめ
この記事では、FastAPIを使ったREST API開発の基本を一通り学びました。
| 学んだこと | ポイント |
|---|---|
| 環境構築 | venv + pip install fastapi uvicorn |
| GETリクエスト | パスパラメータ・クエリパラメータ |
| POSTリクエスト | リクエストボディをPydanticモデルで受け取る |
| バリデーション | Fieldオプションで細かい制約を設定 |
| DB連携 | SQLAlchemy + SQLiteでデータを永続化 |
| Swagger UI |
/docs で自動生成されるドキュメントを活用 |
FastAPIはシンプルなコードで高機能なAPIを素早く構築できるフレームワークです。今回紹介した内容をベースに、認証(JWT)やDockerコンテナ化など、さらに発展的な内容にも挑戦してみてください!
💬 質問や感想があれば、コメント欄でお気軽にどうぞ!
👍 役に立ったら、いいね&ストックをお願いします!
🎓 ここまで読んでくださって、本当にありがとうございました!