1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FastAPI で「Summary 型」を作る — 一覧APIでユーザー情報を返すときの共通モデル

1
Last updated at Posted at 2026-09-08

API を作っていると、ユーザー情報を返す場面がやたら多いことに気づきます。友達一覧、メンバー一覧、メッセージの送信者、検索結果……。

これを場面ごとに考えていると、同じユーザーなのに返ってくる形がバラバラになります。そこで「一覧に埋め込むためのユーザー情報」を専用のモデルとして1つ用意するやり方があります。私は UserSummary という名前を付けていて、この記事では「Summary 型」と呼びます。

FastAPI + Pydantic を例にしますが、考え方はどの言語でも同じです。

ちなみに FastAPI には @router.get(summary="...") という別の機能もあります(/docs の見出しになるやつ)。この記事はそれとは関係なく、レスポンスに使うモデルの設計の話です。

ユーザー情報を返す場面は思ったより多い

まず、実際にどれくらいあるかを見てみます。私が作っているチャットアプリだと、これだけありました。

  • 友達一覧
  • ユーザー検索の結果
  • 友達申請の一覧(送信・受信)
  • QRコードを読み取った結果
  • ルームのメンバー一覧
  • メッセージの送信者
  • 添付ファイルの投稿者

7箇所です。しかも下2つのように、「ユーザーの一覧」ではないのにユーザー情報がくっついてくるパターンもあります。

ここで何も考えずに作ると、こうなりがちです。

// 友達一覧のレスポンス
{ "id": "...", "username": "山田", "icon_url": "...", "created_at": "..." }

// メッセージの送信者
{ "user_id": "...", "name": "山田", "icon": "..." }

// メンバー一覧
{ "id": "...", "username": "山田", "icon_url": "...", "role": "admin" }

同じ人なのに、キーの名前も入っている項目も違います。 こうなると画面を作る側は、場面ごとに別の処理を書くことになります。

最小情報を Summary 型にまとめる

そこで、ユーザーを1行で表示するのに必要な情報だけを集めたモデルを1つ作ります。

class UserSummary(BaseModel):
    id: uuid.UUID          # ユーザーID
    username: str          # 名前
    icon_url: str | None   # アイコン画像
    online_status: str     # オンラインかどうか

たったこれだけです。そして、ユーザー情報を返す場面では全部このモデルを使うと決めます。

class FriendResponse(BaseModel):
    id: int
    user: UserSummary          # ← 埋め込む

class MessageResponse(BaseModel):
    content: str
    sender: UserSummary        # ← ここも同じモデル

図にすると、1枚の共通プロフィールカードを、必要な画面で使い回すイメージです。

user-summary-reuse.png

呼び出し元では usersender と名前が変わっても、その中に入るユーザー情報は UserSummary にそろいます。

返ってくる JSON はこうなります。

// 友達一覧
{ "id": 12, "user": { "id": "...", "username": "山田", "icon_url": "...", "online_status": "online" } }

// メッセージ
{ "content": "こんにちは", "sender": { "id": "...", "username": "山田", "icon_url": "...", "online_status": "online" } }

usersender で名前は違いますが、中身の形はまったく同じです。

こうしておくと、画面側は「ユーザーカード」を表示する部品を1つ作れば、どこでも使い回せます。名刺のフォーマットを統一するようなイメージですね。

自分用と他人用は、モデルごと分ける

もう1つ大事なのが、自分のプロフィールを返すモデルは別に用意することです。

class UserResponse(BaseModel):     # 自分用(GET /users/me)
    id: uuid.UUID
    username: str
    email: str                     # ← 他人には見せない
    icon_url: str | None
    status_message: str | None
    online_status: str
    qr_token: str                  # ← 他人には見せない
    organization_id: uuid.UUID | None
    created_at: datetime
    updated_at: datetime

自分用のほうには、メールアドレスやQRトークンといった本人にしか見せてはいけない情報が入っています。

「1つのモデルにまとめて、他人に返すときだけ項目を消す」というやり方もありますが、おすすめしません。消し忘れたら露出するからです。

モデルごと分けておけば、友達一覧のコードで UserSummary を使っている限り、メールアドレスは構造的に出しようがありません。ここは頑張って気をつけるところではなく、仕組みで防ぐところだと思っています。

何を入れて、何を入れないか

判断に迷ったら、この基準で考えています。

その一覧の1行を描くのに、その項目は要るか?

これで並べると、こうなります。

項目 入れる? 理由
名前 入れる 名前がないと誰だかわからない
アイコン 入れる 一覧で顔が見えるかは体験に直結する
オンライン状態 入れる 一覧に緑の点を出したい
ユーザーID 入れる タップしたときの遷移先に要る
登録日時 入れない 一覧に出す場面がない
組織ID 入れない 表示には使わない
メールアドレス 入れない そもそも他人に見せない

迷ったら「入れない」

もう1つ、迷ったら入れないをおすすめします。

理由は単純で、後から足すのは簡単だけど、消すのは大変だからです。

一度返してしまった項目は、どこかの画面がもう使っているかもしれません。消すには全画面を確認する必要があります。逆に、足すのは既存の画面に影響しません(知らない項目は無視されるだけです)。

なので「使うかもしれないから入れておこう」ではなく、**「使うことがわかってから足す」**のほうが安全です。

実装: 丸ごと渡せるようにしておく

FastAPI(Pydantic v2)なら、こう書けます。

class UserSummary(BaseModel):
    model_config = ConfigDict(from_attributes=True)   # ← これが効く

    id: uuid.UUID
    username: str
    icon_url: str | None = None
    online_status: str

from_attributes=True を付けておくと、データベースから取ってきたユーザーを丸ごと渡せるようになります。

user = db.get(User, user_id)      # データベースから丸ごと取って
UserSummary.model_validate(user)  # 丸ごと渡す

この書き方だと、UserSummary に書いてある項目だけが自動で拾われ、それ以外(メールアドレスなど)は渡しても勝手に捨てられます

一方、こう書いてしまうと後がしんどくなります。

# 手で1つずつ書く書き方
UserSummary(
    id=user.id,
    username=user.username,
    icon_url=user.icon_url,
    online_status=user.online_status,
)

動きは同じですが、項目を1つ足したくなったとき、この書き方をしている全箇所を直す必要があります。7箇所あれば7箇所です。

実際、私のところには後から「ステータスメッセージも一覧に出したい」という要望が来ました。丸ごと渡す書き方にしてあったので、モデルに1行足すだけで7箇所すべてに反映されました。この話は別記事に書きます。

まとめ

  • ユーザー情報を返す場面は思ったより多い。場面ごとに考えると形がバラバラになる
  • 「一覧の1行を描くための最小情報」をモデルにして、全部そこを通す
  • 自分用と他人用はモデルごと分ける。消し忘れによる情報露出を仕組みで防げる
  • 入れるか迷ったら入れない。足すのは簡単、消すのは大変

もし今、ユーザー情報を返す API を書いていて画面ごとにレスポンスの形が違っているなら、まず「どの画面でも共通して出している項目」を書き出してみるのが始めやすいです。だいたい名前とアイコンとIDあたりに落ち着くはずで、それがそのまま最初の Summary 型になります。

参考

関連記事

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?