UUIDを使ったREST API設計で初心者がつまずいた3つのポイント
UUIDを使ったREST API設計で初心者がつまずいた3つのポイント【FastAPI実装例つき】
こんにちは、フロントエンド開発を中心に活動してきたTakaです。
最近、SNSの運用データを集計するKPIダッシュボードを自作するプロジェクトに取り組んでいます。
きっかけは、彼女のInstagramやX(旧Twitter)のフォロワー数を記録・分析してみたかったこと。
手元にスプレッドシートを開いて「これ、いちいち手で打つの大変すぎない?」と気づいたのが始まりでした。
そこで選んだのが FastAPI × Python。
APIでデータを受け取り、スプレッドシートや可視化ツールに渡す仕組みを構築する中で、**「UUIDによるREST API設計」**に挑戦してみたんですが……
案の定、いろいろつまずきました。
この記事では、FastAPI初心者の僕がUUID付きAPIを構築する中で
- 「これ絶対つまずくやん…!」と思ったポイント
- 実際に動かしたコード(CRUD付き)
- UUID設計にしてよかったこと
をまとめています。
UUIDとは?
UUID(Universally Unique Identifier)は、**世界中で一意となる識別子(ID)**のことです。
例えば、データを登録するときに「0番、1番、2番…」と番号で管理していると、削除や並び替えで順番がズレたり、外部に推測されてしまうリスクがあります。
そんなときに便利なのがUUIDです。以下のような、ランダムで重複しない文字列になります:
REST APIをListで管理していた頃
最初はデータをPythonのリスト(List[Item])に保存して、インデックス(0, 1, 2…)を使って管理していました。
fake_items_db: List[Item] = []
このときは単純で、POSTで追加 → GETで一覧取得 → GET /items/0 で個別取得、という流れでした。
でも、これでAPIを運用しようとすると次のような問題が出てきました:
順番がズレると、意図しないデータを取得してしまう
DELETEするとインデックスがずれる
URLに 0, 1, 2 が見えてセキュリティ的にも怖い
そこで、ID管理を「番号」から「UUID」に切り替えることにしました。
UUID管理の実装と各メソッド解説(FastAPI)
🔸 モデル定義 & 簡易DBの準備
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Optional, Dict
import uuid
app = FastAPI()
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
fake_items_db: Dict[str, Item] = {}
🔹 POST:新規登録
@app.post("/items/")
def create_item(item: Item):
item_id = str(uuid.uuid4())
fake_items_db[item_id] = item
return {"item_id": item_id, "item": item}
UUIDを使って一意なIDを生成し、それをキーにして辞書に保存しています。
🔹 GET:全件取得
@app.get("/items/")
def read_all_items():
return fake_items_db
今登録されている全てのアイテムを返します。UUIDがキーとして表示されます。
🔹 GET:UUIDで1件取得
@app.get("/items/{item_id}")
def read_item(item_id: str):
if item_id not in fake_items_db:
raise HTTPException(status_code=404, detail="Item not found")
return fake_items_db[item_id]
UUIDが存在しない場合は404エラーを返します。
🔹 PUT:更新
@app.put("/items/{item_id}")
def update_item(item_id: str, item: Item):
if item_id not in fake_items_db:
raise HTTPException(status_code=404, detail="Item not found")
fake_items_db[item_id] = item
return {"message": "Item updated", "item": item}
UUIDに該当するアイテムを上書きします。
🔹 DELETE:削除
@app.delete("/items/{item_id}")
def delete_item(item_id: str):
if item_id not in fake_items_db:
raise HTTPException(status_code=404, detail="Item not found")
del fake_items_db[item_id]
return {"message": "Item deleted"}
指定したUUIDのデータを削除します。
初心者がつまずいた3つのポイント
💥 ① UUIDを文字列に変換し忘れて比較エラー
item_id = uuid.uuid4()
if item_id not in fake_items_db: # ❌ 比較ができない!
→ 正しくは文字列に変換して使います。
💥 ② List → Dict への切り替えが混乱
インデックスアクセスではなく、キーアクセス(dict[key])に切り替わるため、感覚的に少し慣れが必要です。
💥 ③ UUIDのコピペミスで404多発
Swagger UIでのテスト時にコピペミスして「Item not found」が出るという、地味なハマりどころ。
UUIDにしてよかったこと
IDを推測されない安全設計に近づいた
リスト操作の副作用(順番ズレ、index消滅)から解放
将来のDB連携(SupabaseやMySQL)でも使いやすい
次にやること
今回のAPIはメモリ上の簡易DBで構築していますが、次は SupabaseやMySQLに永続保存できるようにし、VueやGoogle Sheetsと連携する分析ダッシュボードに進化させていく予定です 🙌
この記事が「FastAPIでREST APIを実装してみたい」「UUIDベースの設計に変えてみたい」という方の参考になれば嬉しいです!
📦 完成したコード全体はこちら(GitHub)▶️ TKNRFJK1208/fastapi-uuid
フォロー・コメント励みになります!
▶️ 次回:「FastAPI × SupabaseでSNS分析ダッシュボードを永続化してみた話」
Discussion