概要(ルーティングは「URL → 関数」の地図づくり)
ルーティングは、
「どのURLにアクセスされたら、どの関数(処理)を動かすか」
を決める仕組みです。
Webフレームワーク(FastAPI / Flask / Django など)はすべて、このルーティング機能を持っています。
URLのパス(/users/1 など)、HTTPメソッド(GET / POST など)、クエリパラメータ(?q=python など)を見て、
「このリクエストはこの関数に渡そう」と振り分けてくれます。
あなたは「ルーティングの地図」と「対応する関数」を書くだけで、「Webアプリ」「Web API」っぽい動きを作れるようになります。
ルーティングの基本イメージ(URL と HTTPメソッドで振り分け)
URL と関数を結びつける最小の例(FastAPI)
まずは、FastAPI を例に「最小ルーティング」を見てみます。
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello World"}
@app.get("/health")
def health_check():
return {"status": "ok"}
Pythonこれで、
GET /に来たらread_rootGET /healthに来たらhealth_check
という対応ができます。
ここで大事なのは 2つです。
1つ目は、@app.get("/...") というデコレータ。
「この関数は、GET メソッドでこのパスに来たリクエストを処理します」という宣言です。
2つ目は、URL のパスごとに関数が分かれていること。
1ファイルにどんどんエンドポイントを増やしていくと、「URLと関数の対応表」のようになっていきます。
HTTPメソッド(GET / POST / PUT / DELETE)もルーティングの条件
同じ URL でも、HTTPメソッドが違うと別の処理にしたいことがあります。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items")
def list_items():
return ["apple", "banana"]
@app.post("/items")
def create_item():
return {"message": "created"}
PythonGET /items は「一覧取得」POST /items は「新規作成」
という風に分けるのが、REST風の典型的なスタイルです。
フレームワーク側は、
- パス(
/items) - HTTPメソッド(GET or POST)
の組み合わせで、「どの関数を呼ぶか」を決めています。
パスパラメータ(URL の一部が変数になる部分)
/users/123 の「123」をどう受け取るか
ユーザーIDや商品IDのように、「URL の一部でリソースを指定したい」ケースがあります。
これが「パスパラメータ」です。
FastAPI の例で見てみます。
from fastapi import FastAPI
app = FastAPI()
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {"user_id": user_id}
Pythonこのルートは、
GET /users/1→user_id=1GET /users/10→user_id=10
という風に、{user_id} の部分を変数として受け取れます。
ここで深掘りしたいポイントは 3つです。
1つ目は、ルート文字列に {user_id} と書いていること。
ここが「変化する部分(変数)」として扱われます。
2つ目は、関数の引数名 user_id が、ルートの {user_id} と同じ名前であること。
FastAPI は名前を見て、自動的に対応づけてくれます。
3つ目は、user_id: int と型を書いていること。
ここがものすごく大事で、FastAPI は「これは整数でなければならない」と理解します。/users/abc のように文字列が来たら、「整数じゃない」と判断して 400 番エラーになります。
つまり、「ルーティングの定義」+「型ヒント」で、
URL から引数へのマッピングとバリデーションを同時にやってくれます。
パスパラメータをいつ使うか(設計の考え方)
一般的に、
- リソースを一意に特定する情報(ユーザーID、商品IDなど)
- URL の構造として「そのリソースっぽい」部分
はパスパラメータにするのがよくある設計ですg。
例:
GET /users/123 → ID 123 のユーザーGET /orders/2025-01-01 → その日の注文一覧
など。
「このURLを見ただけで、何を指しているか分かる」ようにしておくと、
自分にも他人にも優しいAPIになります。
クエリパラメータ(? 以降に付けるオプションの条件)
/search?keyword=python&limit=10 の扱い
検索条件やフィルタ、ページングなどは、クエリパラメータで渡すことが多いです。
FastAPI では、関数の引数として追加するだけでクエリパラメータを受け取れます。
from fastapi import FastAPI
app = FastAPI()
@app.get("/search")
def search_items(keyword: str, limit: int = 10, offset: int = 0):
return {
"keyword": keyword,
"limit": limit,
"offset": offset,
"results": []
}
Pythonこのエンドポイントは、
/search?keyword=apple/search?keyword=apple&limit=5/search?keyword=apple&limit=5&offset=10
のように呼べます。
ここでも型とデフォルト値が効いています。
keyword: strは必須(指定がなければエラー)limit: int = 10は省略可能(指定がなければ 10)offset: int = 0も同様
FastAPI は、URL の ? 以降を自動で解析し、
型変換(文字列 → int など)までして引数に渡してくれます。
パスパラメータとクエリパラメータの使い分け
ざっくりとした指針はこうです。
パスパラメータ
→ 「このリソースそのもの」を表す本質的な情報
→ 例:/users/123, /products/abc, /articles/2025/01/01
クエリパラメータ
→ 「どう取得/表示するか」という条件・オプション
→ 例:?page=2, ?sort=price_desc, ?keyword=python
例えば、
GET /users/123 → ユーザー 123 というリソースGET /users?age=20&active=true → 20歳でアクティブなユーザーの検索条件
のようなイメージです。
この使い分けが自然になってくると、
ルーティング設計がかなりスッキリしてきます。
ルーティングをファイルごとに分ける(APIRouter のイメージ)
1ファイルに書き続けると、すぐに破綻する
FastAPI でルートを増やしていくと、こんな感じになりがちです。
# main.py が巨大化…
@app.get("/users")
...
@app.get("/users/{user_id}")
...
@app.post("/users")
...
@app.get("/items")
...
@app.post("/items")
...
Python最初は問題なくても、
「ユーザー系」「商品系」「レポート系」などが混ざってくると、
どこに何があるか分かりづらくなります。
そこで使うのが APIRouter です。
APIRouter で「モジュール単位のルーティング」を作る
構成例を見てみます。
app/
main.py
routers/
__init__.py
users.py
items.py
routers/users.py:
from fastapi import APIRouter
from pydantic import BaseModel
from typing import List
router = APIRouter(prefix="/users", tags=["users"])
class User(BaseModel):
id: int
name: str
fake_users = [
User(id=1, name="Taro"),
User(id=2, name="Hanako"),
]
@router.get("/", response_model=List[User])
def list_users():
return fake_users
@router.get("/{user_id}", response_model=User)
def get_user(user_id: int):
for user in fake_users:
if user.id == user_id:
return user
# 本当は HTTPException を返すのが正解だが、ここでは割愛
raise RuntimeError("not found")
Pythonrouters/items.py も同じように作れます。
main.py:
from fastapi import FastAPI
from routers.users import router as users_router
from routers.items import router as items_router
app = FastAPI()
app.include_router(users_router)
app.include_router(items_router)
Pythonこれで、
/users/...系はrouters/users.py/items/...系はrouters/items.py
という具合に、URL 空間とファイル構造が対応します。
ここでのポイントは、
APIRouter で「このモジュールは users のルートをまとめたものです」と宣言する
main.py は、ルーターを登録するだけの「入口」にする
という構造にすることです。
ルーティングをモジュール単位で整理すると、
アプリが大きくなっても「どのファイルを見ればいいか」が一発で分かるようになります。
自動化 × ルーティングの具体イメージ(既存スクリプトをAPI化)
既存の処理を「ルートにぶら下げる」感覚
例えば、あなたがすでにこんな自動化関数を持っているとします。
# services/report.py
from datetime import date
def generate_daily_report(target_date: date) -> dict:
# 何か重い集計
return {
"date": target_date.isoformat(),
"total_sales": 12345,
"orders": 67,
}
Pythonこれを「Web API から呼べるようにする」には、
ルーティングを 1 つ用意して、その中でこの関数を呼ぶだけです。
# routers/report.py
from fastapi import APIRouter
from pydantic import BaseModel
from datetime import date
from services.report import generate_daily_report
router = APIRouter(prefix="/report", tags=["report"])
class ReportRequest(BaseModel):
date: date
class ReportResponse(BaseModel):
date: date
total_sales: int
orders: int
@router.post("/daily", response_model=ReportResponse)
def daily_report(body: ReportRequest):
result = generate_daily_report(body.date)
return result
Pythonこのルートは、
POST /report/daily に対して{"date": "2025-01-01"} のような JSON を受け取り、
日次レポートを計算して返します。
ルーティングの役割は、
どの URL に来たら
どの関数を呼んで
どんな形の入力・出力を期待するか
を「入口として宣言する」ことです。
中身のビジネスロジック(集計処理やファイルI/Oなど)は、
従来どおり普通の Python 関数として書いておけます。
まとめ(ルーティングは「URL と関数をつなぐ宣言」)
最後に、Python Web フレームワークのルーティングを整理します。
- ルーティングは、「URL と HTTPメソッドに応じて、どの関数を呼ぶか」を決める仕組みで、Webフレームワークの心臓部。
- パスパラメータ(
/users/{id})は「リソースを特定するための情報」を URL の一部として受け取り、型ヒントで自動バリデーションする。 - クエリパラメータ(
?q=python&limit=10)は「検索条件やオプション」を渡すのに使い、関数の引数として自然に受け取れる。 - APIRouter(FastAPI)などで、URL 空間ごとにルートを分割すると、「ユーザー系」「商品系」などに整理されて大規模化にも耐えやすくなる。
- 既存の自動化関数を「ルートにぶら下げる」ことで、スクリプトをそのまま Web API として公開できる。
この感覚が掴めてくると、
「この処理は /tools/xxx、これは /report/yyy かな」
と、自然に URL 設計を考えながらコードを書けるようになります。
