FastAPI入門 — PythonでREST APIを最速で作る方法
この記事でわかること
- WebAPIの基本的な仕組み — リクエストとレスポンスのやり取りを具体例で理解
- FastAPIの環境構築と最初のAPI作成 —
pip installから動作確認までの手順 - GET/POST/PUT/DELETEの実装方法 — CRUDの各操作をコード例付きで解説
- パスパラメーター・クエリパラメーター・リクエストボディの使い分け — いつどれを使うべきか
- 非同期処理と自動ドキュメント生成 — FastAPIならではの便利機能
そもそもWebAPIとは?
WebAPIは、プログラム同士がネットワーク越しにデータをやり取りする仕組みです。
身近な例で説明すると、スマホの乗換案内アプリの裏側はこうなっています。
- リクエスト: スマホ(クライアント)が「A駅からB駅までのルートを教えて」とサーバーに送信
- レスポンス: サーバーが乗換情報をJSON形式で返す
- 表示: アプリが受け取ったデータを画面に表示
この「リクエストを送って、レスポンスを受け取る」のがWebAPIの基本です。
HTTPメソッドとCRUD操作の対応
WebAPIでは、やりたい操作に応じて4種類のHTTPメソッドを使い分けます。
| HTTPメソッド | 操作 | 具体例 |
|---|---|---|
| GET | データの取得 | ユーザー情報の表示 |
| POST | データの登録 | 新規ユーザーの作成 |
| PUT | データの更新 | プロフィールの変更 |
| DELETE | データの削除 | アカウントの削除 |
なぜWebAPIを使うのか
WebAPIでクライアントとサーバーを分離するメリットは2つあります。
1. 変更に強い設計になる サーバー側のデータベースを変更しても、APIの仕様さえ変えなければクライアント側のコードは一切変更不要です。
2. 複数のクライアントから使える 同じAPIをスマホアプリ、Webアプリ、他のサーバーから呼び出せます。機能を一度作れば使い回しが利きます。
FastAPIの環境構築
インストール
pip install fastapi
pip install "uvicorn[standard]"
fastapiがフレームワーク本体、uvicornがASGI対応のWebサーバーです。
最初のAPIを作る
main.pyというファイルを作成し、以下のコードを書きます。
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
return {"message": "Hello API"}
たった5行でAPIが完成します。@app.get("/")はHTTPのGETメソッドでルートパス(/)にアクセスしたときにroot()関数を実行するという意味です。
サーバーの起動
uvicorn main:app --reload
--reloadオプションを付けると、コードを変更するたびに自動でサーバーが再起動します。開発中は常に付けておくと便利です。
起動後、ブラウザで http://127.0.0.1:8000/ にアクセスすると {"message":"Hello API"} が表示されます。
クライアント側からのアクセス
Pythonのrequestsライブラリを使ってAPIを呼び出す場合のコードです。
import requests
response = requests.get("http://127.0.0.1:8000/")
print(response.status_code) # 200
print(response.text) # {"message":"Hello API"}
パスパラメーター — URLの一部を変数にする
URLのパスに変数を埋め込みたい場合に使います。たとえば /items/111 のように、IDをURLに含めてアクセスするパターンです。
サーバー側
@app.get("/items/{item_id}")
def read_item(item_id):
return {"item_id": item_id, "item_name": "Tシャツ"}
{item_id} の部分が変数になります。/items/111 にアクセスすると item_id に "111" が代入されます。
クライアント側
response = requests.get("http://127.0.0.1:8000/items/111")
# {"item_id":"111","item_name":"Tシャツ"}
パス競合の注意点
以下のように固定パスと可変パスが競合する場合、コードの記述順で優先度が決まります。
# この順番で書くこと(固定パスを先に定義)
@app.get("/items/special") # 固定パス(先に定義)
def read_special():
return {"item": "special"}
@app.get("/items/{item_id}") # 可変パス(後に定義)
def read_item(item_id):
return {"item_id": item_id}
順番を逆にすると、/items/special へのアクセスが可変パスのほうにマッチしてしまい、item_id="special" として処理されます。
クエリパラメーター — URLの末尾にフィルター条件を付ける
URLの末尾に ?key=value の形式でパラメーターを渡す方法です。一覧の絞り込みやページネーションで使います。
サーバー側
items = ["りんご", "バナナ", "みかん", "ぶどう", "いちご"]
@app.get("/items")
def read_items(skip: int = 0, limit: int = 10):
return {"items": items[skip:skip+limit]}
デフォルト値を設定しておくと、パラメーターを省略してもエラーになりません。
クライアント側
# 2番目から2件取得
response = requests.get("http://127.0.0.1:8000/items?skip=1&limit=2")
# {"items":["バナナ","みかん"]}
バリデーション(値の検証)
Queryを使うと、受け取る値に制約を付けられます。
from typing import Annotated
from fastapi import Query
@app.get("/items")
def read_items(limit: Annotated[int, Query(ge=1, le=10)] = 10):
# ge=1: 1以上のみ許可、le=10: 10以下のみ許可
return {"items": items[:limit]}
範囲外の値が送られてきた場合、FastAPIが自動で422 Unprocessable Entityエラーを返します。自分でバリデーションコードを書く必要がありません。
リクエストボディ — POSTやPUTでデータを送る
GETではURLにパラメーターを付けましたが、POST/PUTではリクエストボディにJSON形式でデータを含めます。ユーザー登録や商品登録など、複雑なデータを送る場合に使います。
Pydanticモデルでデータ構造を定義する
from pydantic import BaseModel
from typing import Union
class Item(BaseModel):
name: str
price: float
description: Union[str, None] = None
BaseModelを継承したクラスを作ると、FastAPIが受け取ったJSONデータを自動で型チェック・変換してくれます。descriptionは省略可能なフィールドです。
サーバー側
@app.post("/items")
def create_item(item: Item):
print(f"受信データ: {item}")
return item
クライアント側
response = requests.post(
"http://127.0.0.1:8000/items",
json={"name": "Tシャツ", "price": 2000, "description": "白Tシャツ"}
)
priceに文字列を送ると自動でエラーが返ります。型安全なAPIが追加コードなしで実現できます。
ヘッダー — 認証情報やメタデータを送受信する
APIキーやアクセストークンなど、URLには含めたくない情報はHTTPヘッダーに載せます。
リクエストヘッダーの受け取り
from fastapi import Header
from typing import Union
@app.get("/sample")
def read_sample(authorization: Union[str, None] = Header(default=None)):
print(f"Authorization: {authorization}")
return {"message": "認証情報を受信しました"}
クライアント側
response = requests.get(
"http://127.0.0.1:8000/sample",
headers={"Authorization": "Bearer token123"}
)
レスポンスヘッダーの設定
サーバーからクライアントにカスタムヘッダーを返すこともできます。
from fastapi import Response
@app.get("/sample")
def read_sample(response: Response):
response.headers["X-Request-Id"] = "abc-123"
return {"message": "カスタムヘッダー付きレスポンス"}
非同期処理 — 複数の処理を並行実行する
FastAPIはasync/awaitによる非同期処理に対応しています。外部APIの呼び出しやデータベースクエリなど、待ち時間が発生する処理を並行で実行できます。
サーバー側
import asyncio
@app.get("/sleep/{seconds}")
async def sleep_time(seconds: int):
await asyncio.sleep(seconds)
return {"waited": f"{seconds}秒"}
同期と非同期の速度差
2つのリクエスト(1秒待機 + 2秒待機)を同時に発行した場合の違いです。
| 方式 | 合計時間 | 理由 |
|---|---|---|
| 同期処理 | 約3秒 | 1秒待機 → 2秒待機を順番に実行 |
| 非同期処理 | 約2秒 | 両方を同時に開始、遅いほうに合わせて完了 |
データベースへの問い合わせが多いAPIサーバーでは、非同期処理による性能向上が顕著です。
自動ドキュメント生成 — FastAPI最大の強み
FastAPIでAPIを作ると、コードを書くだけでAPIドキュメントが自動生成されます。追加の設定は不要です。
| URL | ドキュメント形式 | 特徴 |
|---|---|---|
/docs |
Swagger UI | ブラウザ上でAPIを試せるインタラクティブUI |
/redoc |
ReDoc | 閲覧用の見やすいドキュメント |
/openapi.json |
OpenAPI仕様 | 他ツールとの連携用JSON |
/docsにアクセスすると、定義したすべてのエンドポイントが一覧表示され、画面上から直接リクエストを送ってレスポンスを確認できます。フロントエンドエンジニアとの連携や、外部にAPIを公開する場合に特に便利です。
FastAPIが選ばれる理由
FastAPIをDjangoやFlaskと比較したとき、以下の点が優位です。
| 項目 | FastAPI | Django REST Framework | Flask |
|---|---|---|---|
| 非同期処理 | ネイティブ対応 | 限定的 | 非対応(拡張が必要) |
| 型チェック | Pydanticで自動検証 | シリアライザーで手動定義 | なし |
| ドキュメント生成 | 自動 | 追加ライブラリ必要 | 追加ライブラリ必要 |
| パフォーマンス | 高速(ASGI) | 中程度(WSGI) | 中程度(WSGI) |
| 学習コスト | 低い | 高い(機能が多い) | 低い |
Netflix、Uber、Microsoftなど大手企業でも本番環境で採用されており、GitHubスター数は80,000を超える人気フレームワークです。
「小〜中規模のAPIを素早く作りたい」「型安全なAPIを最小限のコードで実現したい」という場面でFastAPIは最適な選択肢です。
まとめ
FastAPIの基本的な使い方を整理します。
| やりたいこと | 使う機能 | 例 |
|---|---|---|
| URL内の変数取得 | パスパラメーター | /items/{item_id} |
| 検索・フィルター | クエリパラメーター | ?skip=0&limit=10 |
| データ送信 | リクエストボディ | json={"name": "..."} |
| 認証情報の送受信 | ヘッダー | Authorization: Bearer ... |
| 並行処理 | async/await | await asyncio.sleep(1) |
| ドキュメント確認 | 自動生成 | /docs にアクセス |
FastAPIは「Pythonの型ヒントを活用して、APIの実装とドキュメントを同時に生成する」という設計思想のフレームワークです。まずはpip install fastapi uvicornして、5行のHello APIから始めてみてください。