🤖

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