0
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の初心者がハマりやすい落とし穴集

0
Posted at

FastAPI Logo

画像出典: FastAPI公式

FastAPIはかなり書きやすいフレームワークです。
最初のAPIを作るところまではスムーズに進みやすいので、PythonでAPI開発を始める人にとってとても入りやすい選択肢だと思います。

ただ、その書きやすさゆえに、なんとなく動いたコードのまま進めてしまって、あとで設計や保守で苦しくなることもよくあります。
特に初心者のうちは、エラーの意味がわからない、動いてはいるけど書き方が正しいのか不安、なぜかコードが増えるほど見通しが悪くなる、といった壁にぶつかりがちです。

この記事では、FastAPIを学び始めた人がハマりやすい落とし穴をまとめます。
最初の段階で意識しておくだけで、かなりラクになるはずです。

1. とりあえず dict で受ける癖がつく

最初によくあるのが、リクエストボディをなんでも dict で受けてしまうことです。

たしかに dict で受ければ、とりあえず値は取れます。
でもこの書き方を続けると、どんなデータを受け取るAPIなのかがコードから見えにくくなります。さらに、型のずれや必須項目の漏れも、自分で毎回チェックしなければいけません。

FastAPIの強みは、Pydanticモデルで入力を定義できることです。
ここを使わないまま進めると、FastAPIの良さをかなり捨てることになります。

悪い例はこんな感じです。

@app.post("/users")
def create_user(user: dict):
    name = user.get("name")
    age = user.get("age")
    return user

最初からモデルを切っておくと、APIの仕様がかなり見やすくなります。

from pydantic import BaseModel

class UserCreate(BaseModel):
    name: str
    age: int

@app.post("/users")
def create_user(user: UserCreate):
    return user

最初は少し面倒に見えても、あとから必ず助かります。

2. バリデーションを全部手作業で書こうとする

初心者のうちは、値のチェックを if 文で頑張りすぎることがあります。

たとえば、空文字ではないか、数値かどうか、必須項目があるか、といったことを関数の中で全部チェックし始めると、すぐにAPIごとのロジックが重たくなります。
しかも、同じようなチェックがいろいろな場所に散らばります。

FastAPIでは、入力の型や構造をモデルで表現しやすいので、基本的な検証はなるべくモデル側に寄せたほうがきれいです。
関数の中では、業務ロジックに集中できる形にしておくほうが後から読みやすくなります。

初心者のうちは、バリデーションは関数の中で書くもの、と思いがちですが、FastAPIではその発想を少し変えたほうがうまくいきます。

3. response_model を使わず、そのまま返してしまう

これもかなり多いです。
とりあえずレスポンスが返ればOK、という状態で進めてしまうと、返すデータの設計が曖昧なままになります。

特に危ないのが、内部で使っている情報をそのまま返してしまうケースです。
たとえばユーザー情報を返すAPIで、内部モデルに含まれていた不要な値までそのまま外へ出してしまうことがあります。

こんな形です。

class UserInDB(BaseModel):
    id: int
    name: str
    email: str
    password: str

@app.get("/users/{user_id}")
def get_user(user_id: int):
    return UserInDB(
        id=user_id,
        name="Taro",
        email="taro@example.com",
        password="secret"
    )

この書き方だと、意図せず password まで返してしまう可能性があります。

response_model を使えば、公開用のレスポンスをきちんと分けられます。

class UserPublic(BaseModel):
    id: int
    name: str
    email: str

@app.get("/users/{user_id}", response_model=UserPublic)
def get_user(user_id: int):
    return UserInDB(
        id=user_id,
        name="Taro",
        email="taro@example.com",
        password="secret"
    )

FastAPIでは、入力だけでなく出力もちゃんと設計する意識が大事です。

4. async def をなんとなく使う

FastAPIを触り始めると、なんとなく全部 async def にしたくなることがあります。
でも、ここは雰囲気で決めないほうがいいです。

非同期処理は便利ですが、何でも async def にすれば速くなるわけではありません。
使っているライブラリが非同期対応しているかどうかを見て判断する必要があります。

たとえば、await できるHTTPクライアントや非同期対応ライブラリを使うなら async def にする意味があります。
一方で、同期的なDBライブラリや普通の処理しかしていないなら、無理に async def にしなくても大丈夫です。

初心者のうちは、async def のほうが上級っぽく見えるので選びたくなりますが、実際には使い分けのほうが大事です。
背伸びして全部非同期に寄せるより、何が非同期向きなのかを理解するほうがずっと重要です。

5. Depends を使わず、共通処理をコピペする

APIを何本か作っていくと、似たような前処理が増えてきます。
たとえば認証チェック、DBセッションの取得、共通のクエリパラメータ処理などです。

このとき初心者がやりがちなのが、各エンドポイントへそのままコピペしてしまうことです。
最初は動くのですが、修正箇所が増えてどんどんつらくなります。

FastAPIには Depends があるので、共通処理はできるだけそこへ寄せたほうがきれいです。

from fastapi import Depends

def common_query(q: str | None = None):
    return {"q": q}

@app.get("/items")
def read_items(common = Depends(common_query)):
    return common

最初は少し回りくどく見えるかもしれません。
でも、APIが増えるほどありがたさが出てきます。
初心者ほど、コードの重複は早めに潰す癖をつけておくと後がラクです。

6. 422エラーで毎回止まる

FastAPI初心者が高確率で遭遇するのが、422エラーです。

最初は 500 エラーや 404 エラーはなんとなくイメージしやすいのですが、422 は少し戸惑いやすいと思います。
でも、これはFastAPIが入力データをしっかり検証している証拠でもあります。

多くの場合、原因は次のどれかです。

  • リクエストボディのキー名が違う
  • 型が違う
  • 必須項目が足りない
  • パスパラメータやクエリパラメータの渡し方が違う

初心者のうちは、422が出るとAPI本体のロジックが壊れている気がして焦ります。
でも実際は、リクエストの形が合っていないだけということがかなり多いです。

ここで大事なのは、エラーメッセージをちゃんと読むことです。
FastAPIは比較的わかりやすく、どこが不正かを返してくれます。
エラーを見た瞬間にコード全体を疑うのではなく、まずは送っている値の形を確認するだけで解決することも多いです。

7. DBセッションの扱いが雑になる

データベース連携を始めたあたりから、コードが急に複雑になったと感じる人は多いと思います。
その原因の1つが、DBセッションの扱いです。

初心者のうちは、接続をどこで作って、どこで閉じるのかが曖昧なまま進みがちです。
すると、ルーティング関数の中に接続処理が増えたり、セッション管理がバラバラになったりします。

このあたりも、FastAPIでは依存関係として切り出して管理する発想がとても大事です。
DB周りは最初からきれいに書こうとしなくても大丈夫ですが、少なくとも各エンドポイントで好き勝手に接続を扱わないことは意識しておいたほうがいいです。

雑に始めると、あとからトランザクションや例外処理、クローズ漏れで苦しくなります。

8. ルーティング関数に全部書いてしまう

FastAPIは少ないコードで動くので、ルーティング関数の中へそのまま何でも書けてしまいます。
これが便利な反面、初心者がハマりやすい落とし穴でもあります。

よくあるのは、1つの関数の中に

  • リクエストの受け取り
  • バリデーション
  • DB処理
  • 認証チェック
  • レスポンス整形

を全部詰め込んでしまうことです。

最初はそれでも動きます。
でも、少し大きくなると一気に読みにくくなります。
ルーティング関数は、できるだけ入口として薄くしておくほうが見通しがよくなります。

初心者のうちは、どこまで分けるべきか迷うと思います。
ただ、少なくとも1つの関数が長くなり始めたら危険信号です。
APIの入り口、共通処理、業務ロジック、レスポンス設計を少しずつ分けていく意識が大切です。

9. 自動ドキュメントがあるのでテストしなくていいと思ってしまう

FastAPIは /docs で手軽に試せるので、ついそこでの確認だけで満足しがちです。
これは初心者あるあるです。

もちろん、自動ドキュメントはとても便利です。
でも、それだけで十分かというと別です。
手で触って動いたことと、テストで再現できることは違います。

APIが増えてくると、変更のたびに毎回手で試すのはつらくなります。
だからこそ、早い段階でテストを書く習慣を少しずつ持っておくほうがいいです。

最初から完璧なテストを書く必要はありません。
まずは正常系だけでもいいので、自動で確認できる形を少しずつ増やすと、あとで安心感がかなり違います。

10. 認証を自己流で作り始める

認証まわりは、初心者が特に事故りやすいポイントです。
しかも、最初は動いているように見えるので厄介です。

セッション、トークン、OAuth2、JWT など、認証にはいろいろな考え方があります。
ここを雰囲気で実装し始めると、どこかで整合性が崩れます。

FastAPIにはセキュリティ関連の仕組みが用意されているので、認証はなるべく標準的な流れに沿って組み立てたほうが安全です。
自分で全部考えるより、よく使われる構成をベースに理解していくほうが、結果的に近道になりやすいです。

初心者の段階では、認証機能をすぐ自作のオリジナル仕様にしないことが大事です。
まずは定番の形を知って、そこから必要に応じて調整するほうが失敗しにくいです。

まとめ

FastAPIは、初心者でも始めやすいフレームワークです。
でも、始めやすいからこそ、なんとなく書けたコードのまま進めてしまいやすい面もあります。

特にハマりやすいのは、次のようなポイントです。

  • dict で受け続ける
  • バリデーションを手作業で抱え込む
  • response_model を使わない
  • async def を雰囲気で選ぶ
  • Depends を使わずコピペが増える
  • 422エラーの意味がつかめない
  • DBセッション管理が雑になる
  • ルーティング関数が太る
  • 自動ドキュメントだけで安心する
  • 認証を自己流で作り始める

FastAPIは、型、バリデーション、依存性注入、レスポンス設計あたりをちゃんと使うだけで、かなりきれいに書けるようになります。
最初のうちに少しだけ意識しておくと、あとからコードが増えてもだいぶラクです。


お知らせ

以下は自分が出しているUdemy講座で、30日間使える半額クーポン付きリンクです。
FastAPIを体系的に学びたい方は、よければこちらもどうぞ。

Udemy講座はこちら

※価格やクーポンの適用状況は、Udemy側の表示をご確認ください。

0
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
0
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?