FastAPI を触っていると、こんなコードに出会います。
from typing import Annotated
from fastapi import FastAPI, Header
app = FastAPI()
@app.get("/items")
async def read_items(
user_agent: Annotated[str | None, Header()] = None
):
return {"User-Agent": user_agent}
これを見たとき、私はこう感じました。
Header()って何?- なぜ引数に書くだけでヘッダーが取得できるの?
@app.get()と何か関係ある?- デコレーターが特別なことをしている?
この記事では、どう動いているのか、分解してみたいと思います。
Contents
結論
FastAPI が、関数を呼び出す前にHTTP ヘッダーを読み取る処理を自動で差し込んでいる
関数はヘッダーを読んでいません。
FastAPI が読んでから渡しています。
Header() はデコレーターではない
重要なポイント。
Header()
はデコレーターではありません。
これはただの「設定オブジェクト」を返す関数で、
「この引数は HTTP ヘッダーから取得してね」
と FastAPI に伝えているだけです。
@app.get() がサーバー起動時にしていること
@app.get() は単にルーティングを登録しているだけではありません。
サーバー起動時に、次のことをしています。
- 関数の引数(シグネチャ)を読み取る
- 型ヒントを確認する
Header()が付いていることを見つける- 「この引数はヘッダーから取得する」と記録する
この時点ではまだヘッダーは見ていません。
「どう処理するかの設計図」を作っているだけです。
リクエスト時に実際に起きること
リクエストが来ると、FastAPI はあなたの関数をそのまま呼びません。
内部で生成した「ラッパー関数」が先に実行されます。
そのラッパー関数がやっていることは、概念的には次のような処理です。
# ① ヘッダーを読む
value = request.headers.get("user-agent")
# ② 型チェックやバリデーション
# ③ あなたの関数を呼び出す
await read_items(user_agent=value)
つまり、
ヘッダーの判定は、デコレートされた結果できた処理の中で行われる
あなたの関数の中ではありません。
Header() を外すとどうなるか
@app.get("/items")
async def read_items(user_agent: str | None = None):
return {"User-Agent": user_agent}
この場合、FastAPI は
「ただの引数だな」
と判断するため、何も値を入れてくれません。
Header() があることで初めて、
「この値は HTTP ヘッダーから取得する」
というロジックが組み込まれます。
時系列で整理
サーバー起動時
@app.get()が関数を解析Header()を発見- 「このエンドポイントではヘッダーを読む」という処理手順を登録
リクエスト時
- FastAPI のラッパー関数が実行
- ヘッダーを読み取る
- 値を引数にセット
- 関数を呼ぶ
処理のイメージ図
HTTPリクエスト
↓
FastAPIラッパー(ここでHeaderを読む)
↓
あなたの関数
なぜ Annotated を使うのか
以前は次のように書いていました。
user_agent: str | None = Header(default=None)
現在の推奨は:
user_agent: Annotated[str | None, Header()] = None
理由は、
「型」と「どこから取得するか」の情報を分離できるから
FastAPI がより正確に解析できるようになっています。
まとめ
Header() は魔法ではありません。
- デコレーターではない
- 関数の中でヘッダーを読んでいるわけでもない
FastAPI が、関数を実行する前にヘッダーを読む処理を自動で差し込んでいるだけです。

