7日目のゴール
7日目のテーマは
「ここまで学んだ requests+API 設計を“1つの完成アプリ”としてまとめる」 ことです。
今日できるようになってほしいのは、次のイメージです。
外部APIと話す「APIクライアント層」
アプリに必要な機能をまとめる「サービス層」
ユーザーと対話する「アプリ層」
この三つを意識して、
「小さな API ダッシュボードアプリ」を完成させます。
使うAPIは、これまでと同じ https://jsonplaceholder.typicode.com です。
ユーザー一覧、TODO、記事をまとめて扱えるコンソールアプリを作ります。
全体像を言葉で設計する
どんなアプリを作るか
7日目で作るのは、
「JSONPlaceholder ダッシュボード(コンソール版)」です。
やりたいことは、こういう感じです。
ユーザー一覧を取得して表示する
ユーザーIDを指定して、その人の TODO を一覧表示する
ユーザーIDを指定して、その人の記事(posts)を一覧表示する
エラーが起きても落ちずにメッセージを出す
これを、
APIクライアント層
サービス層
アプリ層
に分けて書いていきます。
APIクライアント層 request_json の完成形
すべてのHTTP通信をここに閉じ込める
まずは「外部APIと話す窓口」を一か所にまとめます。
ここに requests とエラー処理を全部押し込めて、
アプリ側には「成功かどうか」と「データ or エラー」を返すだけにします。
# api_client.py
import requests
def request_json(method, url, params=None, json_body=None, headers=None, timeout=5.0):
try:
response = requests.request(
method=method,
url=url,
params=params,
json=json_body,
headers=headers,
timeout=timeout,
)
except requests.exceptions.Timeout:
return {
"ok": False,
"error_type": "timeout",
"status_code": None,
"message": "タイムアウトしました。サーバーの応答がありません。",
}
except requests.exceptions.RequestException as e:
return {
"ok": False,
"error_type": "network",
"status_code": None,
"message": f"通信エラーが発生しました: {e}",
}
if not (200 <= response.status_code < 300):
return {
"ok": False,
"error_type": "http",
"status_code": response.status_code,
"message": f"サーバーからエラー応答が返されました。(ステータスコード: {response.status_code})",
}
try:
data = response.json()
except ValueError:
return {
"ok": False,
"error_type": "json",
"status_code": response.status_code,
"message": "レスポンスをJSONとして解釈できませんでした。",
}
return {
"ok": True,
"error_type": None,
"status_code": response.status_code,
"data": data,
}
Pythonここでの重要ポイントは三つです。
例外(タイムアウト・通信エラー)を外に投げず、ここで吸収していること。
HTTPステータスコードが 200〜299 以外なら「エラー」として扱っていること。
成功時と失敗時を ok フラグと data / message で表現していること。
これで、アプリ側は try / except を書かなくても、result["ok"] を見るだけで成功かどうか判断できます。
サービス層で「このアプリが使うAPI」をまとめる
BASE_URL とエンドポイントをここに集約する
次に、「このアプリで使うAPI」をまとめる層を作ります。
ここでは、どのURLを叩くか、どのデータを返すかだけを担当します。
# api_service.py
from api_client import request_json
BASE_URL = "https://jsonplaceholder.typicode.com"
def build_url(path: str) -> str:
return f"{BASE_URL}{path}"
def fetch_users():
url = build_url("/users")
result = request_json("GET", url)
if not result["ok"]:
return None, result["message"]
return result["data"], None
def fetch_todos_by_user(user_id: int):
url = build_url("/todos")
params = {"userId": user_id}
result = request_json("GET", url, params=params)
if not result["ok"]:
return None, result["message"]
return result["data"], None
def fetch_posts_by_user(user_id: int):
url = build_url("/posts")
params = {"userId": user_id}
result = request_json("GET", url, params=params)
if not result["ok"]:
return None, result["message"]
return result["data"], None
Pythonここでの重要ポイントは、
BASE_URL を一か所にまとめていること。
エンドポイントごとに「URL+パラメータ+戻り値の形」を決めていること。
アプリ側には「データ or None」と「エラーメッセージ or None」を返していること。
アプリ層は、
「外部APIのURL」や「HTTPメソッド」を知らなくてよくなります。
表示用の整形ロジックを分ける
生のJSONをそのまま print しない
APIから返ってくるデータは、
そのままだと情報が多すぎたり、見づらかったりします。
そこで、「人間に見せる形」に整形する関数を用意します。
# formatters.py
def format_users(users: list) -> str:
lines = []
lines.append(f"ユーザー数: {len(users)}")
lines.append("-----")
for user in users:
line = f"[{user['id']}] {user['name']} ({user['email']})"
lines.append(line)
return "\n".join(lines)
def format_todos(todos: list, user_id: int) -> str:
lines = []
lines.append(f"userId={user_id} の TODO は {len(todos)} 件")
lines.append("-----")
for todo in todos:
status = "✔" if todo["completed"] else "…"
line = f"{status} [{todo['id']}] {todo['title']}"
lines.append(line)
return "\n".join(lines)
def format_posts(posts: list, user_id: int, limit: int = 5) -> str:
lines = []
lines.append(f"userId={user_id} の記事は {len(posts)} 件")
lines.append(f"最初の {min(limit, len(posts))} 件を表示")
lines.append("-----")
for post in posts[:limit]:
lines.append(f"[{post['id']}] {post['title']}")
body_preview = post["body"].replace("\n", " ")
lines.append(f" {body_preview[:60]}...")
lines.append("-----")
return "\n".join(lines)
Pythonここでの本質は、
「APIの生データ」と「ユーザーに見せる文字列」を分けていること。
この整形関数は、
コンソールでもGUIでも、そのまま再利用できます。
アプリ層:メニュー付きダッシュボードを作る
ユーザーと対話する「顔」の部分
最後に、ユーザーと対話するメインスクリプトを書きます。
ここでは、サービス層とフォーマッタを呼び出すだけに集中します。
# app_dashboard.py
from api_service import fetch_users, fetch_todos_by_user, fetch_posts_by_user
from formatters import format_users, format_todos, format_posts
def input_user_id() -> int | None:
user_id_str = input("ユーザーIDを入力してください: ").strip()
if not user_id_str.isdigit():
print("数字を入力してください。")
return None
return int(user_id_str)
def show_users():
users, error = fetch_users()
if error is not None or users is None:
print("ユーザー一覧の取得に失敗しました。")
print("詳細:", error)
return
text = format_users(users)
print()
print(text)
def show_todos_by_user():
user_id = input_user_id()
if user_id is None:
return
todos, error = fetch_todos_by_user(user_id)
if error is not None or todos is None:
print("TODO の取得に失敗しました。")
print("詳細:", error)
return
text = format_todos(todos, user_id)
print()
print(text)
def show_posts_by_user():
user_id = input_user_id()
if user_id is None:
return
posts, error = fetch_posts_by_user(user_id)
if error is not None or posts is None:
print("記事の取得に失敗しました。")
print("詳細:", error)
return
text = format_posts(posts, user_id)
print()
print(text)
def main():
while True:
print()
print("=== JSONPlaceholder ダッシュボード ===")
print("1: ユーザー一覧を表示")
print("2: ユーザーIDから TODO 一覧を表示")
print("3: ユーザーIDから 記事一覧を表示")
print("0: 終了")
choice = input("番号を選んでください: ").strip()
if choice == "1":
show_users()
elif choice == "2":
show_todos_by_user()
elif choice == "3":
show_posts_by_user()
elif choice == "0":
print("終了します。")
break
else:
print("不正な入力です。")
if __name__ == "__main__":
main()
Pythonここでの重要ポイントは三つです。
アプリ層は、requests を一切知らないこと。
「何をしたいか」に名前を付けた関数(show_users など)だけで構成されていること。
エラーが起きても、サービス層から返ってきたメッセージを表示するだけで済んでいること。
7日目で絶対に押さえてほしい本質
「APIアプリを“層”で考えると、一気に楽になる」
今日いちばん大事なのは、
頭の中の構造がこうなっていることです。
外部APIと話すのは api_client(request_json)だけ。
どのエンドポイントをどう使うかは api_service が決める。
ユーザーとのやりとりと表示は app_dashboard と formatter が担当する。
この分け方ができると、
APIが増えても、サービス層を足すだけで済む
表示方法を変えたくなったら、フォーマッタだけを直せばいい
GUIにしたくなったら、「アプリ層」を tkinter に置き換えればいい
という、拡張しやすい形になります。
ここまで来たあなたは、
もう「requests を知っている人」ではなく、
「外部APIを設計してアプリに組み込める人」 です。
あとは、
自分が本当に使いたいAPI(天気、ニュース、翻訳、地図など)を一つ選んで、
今日の構造をそのまま当てはめてみてください。
それをやった瞬間に、「学習用のコード」から「自分のためのツール」に変わります。
そこから先が、いちばん楽しいゾーンです。


