概要
FastAPIは、Pythonで高性能なAPIを構築するためのWebフレームワークです。以下の特徴があります。
- 型ヒントベース: Pythonの型ヒントを活用し、リクエスト・レスポンスの自動バリデーションを実現
- 自動ドキュメント: Swagger UI(OpenAPI)が自動生成され、ブラウザからAPIをテスト可能
- 高速: Starlette + Pydanticをベースにしており、Node.jsやGoに匹敵するパフォーマンス
この記事では、FastAPIを使ってTODOアプリのCRUD APIを作成する手順を解説します。
前提条件
環境構築
まず、プロジェクトを作成し、必要なパッケージをインストールします。
1
2
3
| uv init fastapi-todo
cd fastapi-todo
uv add fastapi uvicorn
|
fastapiがWebフレームワーク本体、uvicornがASGIサーバーです。
Hello World
まずは最小構成でFastAPIの動作を確認します。main.pyを作成してください。
1
2
3
4
5
6
7
8
| from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello, FastAPI!"}
|
サーバーを起動します。
1
| uv run uvicorn main:app --reload
|
--reloadオプションを付けると、コードの変更時に自動でリロードされます。
ブラウザで http://localhost:8000 にアクセスすると、以下のレスポンスが返ります。
1
| {"message": "Hello, FastAPI!"}
|
Swagger UIの確認
FastAPIの大きな魅力は、自動生成されるAPIドキュメントです。http://localhost:8000/docs にアクセスすると、Swagger UIが表示されます。
ここからAPIを直接テストできるため、開発中はcurlやPostmanなしでも動作確認が可能です。
CRUD APIの実装
TODOアプリのCRUD APIを実装していきます。
モデルの定義
まず、PydanticモデルでTODOのデータ構造を定義します。main.pyを以下のように書き換えてください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class TodoCreate(BaseModel):
title: str
completed: bool = False
class TodoResponse(BaseModel):
id: int
title: str
completed: bool
todos: dict[int, dict] = {}
next_id: int = 1
|
TodoCreate: 新規作成・更新時のリクエストボディTodoResponse: レスポンスの型定義todos: インメモリのデータストア(dict)next_id: IDの自動採番用カウンター
作成(POST)
1
2
3
4
5
6
7
| @app.post("/todos", response_model=TodoResponse)
def create_todo(todo: TodoCreate):
global next_id
todo_data = {"id": next_id, "title": todo.title, "completed": todo.completed}
todos[next_id] = todo_data
next_id += 1
return todo_data
|
response_model=TodoResponseを指定することで、レスポンスの型がSwagger UIに反映されます。
一覧取得(GET)
1
2
3
| @app.get("/todos", response_model=list[TodoResponse])
def list_todos():
return list(todos.values())
|
個別取得(GET)
1
2
3
4
5
| @app.get("/todos/{todo_id}", response_model=TodoResponse)
def get_todo(todo_id: int):
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")
return todos[todo_id]
|
存在しないIDの場合はHTTPExceptionで404エラーを返します。
更新(PUT)
1
2
3
4
5
6
7
| @app.put("/todos/{todo_id}", response_model=TodoResponse)
def update_todo(todo_id: int, todo: TodoCreate):
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")
todo_data = {"id": todo_id, "title": todo.title, "completed": todo.completed}
todos[todo_id] = todo_data
return todo_data
|
削除(DELETE)
1
2
3
4
5
6
| @app.delete("/todos/{todo_id}")
def delete_todo(todo_id: int):
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")
del todos[todo_id]
return {"detail": "Todo deleted"}
|
完成したコード
上記をすべてまとめたmain.pyの全体像は以下のとおりです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
| from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class TodoCreate(BaseModel):
title: str
completed: bool = False
class TodoResponse(BaseModel):
id: int
title: str
completed: bool
todos: dict[int, dict] = {}
next_id: int = 1
@app.post("/todos", response_model=TodoResponse)
def create_todo(todo: TodoCreate):
global next_id
todo_data = {"id": next_id, "title": todo.title, "completed": todo.completed}
todos[next_id] = todo_data
next_id += 1
return todo_data
@app.get("/todos", response_model=list[TodoResponse])
def list_todos():
return list(todos.values())
@app.get("/todos/{todo_id}", response_model=TodoResponse)
def get_todo(todo_id: int):
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")
return todos[todo_id]
@app.put("/todos/{todo_id}", response_model=TodoResponse)
def update_todo(todo_id: int, todo: TodoCreate):
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")
todo_data = {"id": todo_id, "title": todo.title, "completed": todo.completed}
todos[todo_id] = todo_data
return todo_data
@app.delete("/todos/{todo_id}")
def delete_todo(todo_id: int):
if todo_id not in todos:
raise HTTPException(status_code=404, detail="Todo not found")
del todos[todo_id]
return {"detail": "Todo deleted"}
|
動作確認
サーバーを起動して、Swagger UIから動作確認を行います。
1
| uv run uvicorn main:app --reload
|
http://localhost:8000/docs にアクセスし、以下の順にテストしてください。
1. TODOの作成
POST /todosを開き、「Try it out」をクリックして以下のJSONを入力します。
1
2
3
4
| {
"title": "買い物に行く",
"completed": false
}
|
「Execute」をクリックすると、以下のようなレスポンスが返ります。
1
2
3
4
5
| {
"id": 1,
"title": "買い物に行く",
"completed": false
}
|
2. 一覧取得
GET /todosを実行すると、作成したTODOの一覧が返ります。
3. 更新
PUT /todos/1でcompletedをtrueに変更してみましょう。
1
2
3
4
| {
"title": "買い物に行く",
"completed": true
}
|
4. 削除
DELETE /todos/1を実行すると、TODOが削除されます。GET /todosで空の配列が返ることを確認してください。
まとめ
この記事では、FastAPIを使ってTODO管理のCRUD APIを作成しました。FastAPIの主な利点をまとめます。
- Pydanticモデルによるリクエスト・レスポンスの自動バリデーション
- Swagger UIによるインタラクティブなAPIドキュメント
- Pythonの型ヒントだけでAPIの仕様が定義できるシンプルさ
今回はインメモリのデータストアを使いましたが、実際のアプリケーションではSQLAlchemyなどのORMを使ってデータベースと連携することになります。
関連記事