採点されません。匿名です。 間違えることは学習の一部です。読むだけでもかまいません。

L11 ch03 — POST リクエスト

テクノロジ系 / L11 fastapi-basic — FastAPI 入門

L11 ch03 — POST リクエスト

クライアントから JSON を受け取る

ゴール

  • @app.post() で POST エンドポイント
  • リクエストボディの取得
  • ステータスコード制御 (201 Created)

Request 経由 (基本)

from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/tasks", status_code=201)
async def create_task(request: Request):
    body = await request.json()
    return {
        "title": body["title"],
        "done": body.get("done", False),
    }

async def + await が必須

動作確認 (curl)

curl -X POST http://localhost:8000/tasks \
  -H "Content-Type: application/json" \
  -d '{"title": "買い物"}'

# {"title": "買い物", "done": false}

Swagger UI で操作

http://localhost:8000/docs で POST エンドポイントを 画面から実行可能

  • パラメータ入力欄
  • Try it out → Execute
  • レスポンス確認

ステータスコード

Code意味用途
200OKGET 成功
201CreatedPOST で新規作成
204No ContentDELETE 成功 (body 無し)
400Bad Requestクライアント不正
422Unprocessable Entity型違反
500Internal Server Errorサーバー側エラー

デフォルト値

new_task = {
    "title": body["title"],
    "done": body.get("done", False),  # 省略時 False
}

dict.get(key, default)キー欠落 + デフォルト を一発処理。

Pydantic との接続

L12 で Pydantic モデルを使うと 型安全 + 自動バリデーションに進化。

class TaskIn(BaseModel):
    title: str
    done: bool = False

@app.post("/tasks")
def create(task: TaskIn):  # ← この型で自動検証
    return task

BaseModel を使う完全形(推奨パターン)

Request.json() ではなく、Pydantic BaseModel を引数の型ヒントに置くのが FastAPI の本来の使い方。

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class EchoPayload(BaseModel):
    text: str
    repeat: int = 1            # デフォルト値あり = 省略可能

@app.post("/echo", status_code=201)
def echo(payload: EchoPayload):    # ← async ではなく普通の def でOK
    return {
        "echoed": payload.text * payload.repeat,
    }
curl -X POST http://localhost:8000/echo \
  -H "Content-Type: application/json" \
  -d '{"text": "hi", "repeat": 3}'
# {"echoed": "hihihi"}
  • 引数の型に BaseModel を書くだけで JSON → Python オブジェクトに自動変換
  • payload.text のように属性アクセスできる (dict の body["text"] よりタイポに強い)
  • text 欠落 / repeat に文字列を入れる → 自動で 422 + 詳細なエラー JSON
  • async def / await request.json() 不要 → コードが短くなる

> 今後 POST / PATCH を書くときは 基本このパターンRequest.json() は「型を決められない / 動的なキー」のときだけ。

drill

drill/ — POST + status_code=201

ハンズオン

ch03-post 演習ガイド

受講生向け演習問題集

採点の仕組み (読んでから始めてください)

採点テストは 「お題の通りに動くか」 を機械的にチェックします。

ドリルごとに 3つのタイプ があるので、各ドリル冒頭の 【タイプ】 を確認してください。

タイプ fill (穴埋め問題) — 文法・APIを知っているか

  • ___ (3個並んだアンダースコア) の空欄を埋める
  • 既存の構造は そのまま
  • ロジック自由度: 低 (お題どおりに直す)

タイプ add (機能追加問題) — 既存コードに機能を足せるか

  • 既存コードを残したまま、新しいエンドポイント/機能を 追加 する
  • 実装方法は自由 (関数名・変数名・内部ロジックは好きに)
  • ただしテストが要求する I/F (URL・HTTPメソッド・レスポンスのキー名) は守る
  • 既存テストを壊さないこと

タイプ build (ゼロベース実装問題) — 仕様から実装を組み立てられるか

  • ほぼ空の main.py から 自力で全部書く
  • ファイル構造・関数分割・命名・内部ロジック: 完全に自由
  • 守るのはインターフェースだけ: URL・HTTP メソッド・レスポンス JSON 形式
  • 一番ハードだが、実装力が一番つくタイプ

3タイプ共通で守ること

  • ファイル名 (main.py) は変えない
  • tests/ フォルダは触らない (採点ロジック)

このガイドのドリル一覧

#ドリル名タイトルタイプ難易度
101-fill-request-body@app.post + Request.json() - リクエストボディ受け取り🔧 穴埋め
202-build-user-apiパスパラメータ + POST + HTTPException - User API を白紙から🛠️ ゼロ実装★★

> 詰まったら ヒント ページを開く。それでも分からなければ講師に質問。

ドリル 1: @app.post + Request.json() - リクエストボディ受け取り

ドリル名: 01-fill-request-body

> 🔧 【タイプ: 穴埋め問題】 ___ の部分を埋めて完成させてください。既存のコード構造は変えないこと。

___ を埋めて、POST エンドポイントを完成させてください。

  • Request を使ってボディを受け取る
  • done が省略された場合は False をデフォルトにする
  • ステータスコードは 201 を返す
from fastapi import FastAPI, ___

app = FastAPI()

@app.post("/tasks", status_code=___)
async def create_task(request: ___):
    body = await request.___()
    new_task = {
        "title": body["title"],
        "done": body.___(  "done", False),
    }
    return new_task

ドリル 1: ヒント

ドリル名: 01-fill-request-body

> ⚠️ 自力で考えてから読んでください

  • from fastapi import FastAPI, RequestRequest もインポートする
  • status_code=201 — 作成成功のステータスコード
  • async def + await request.json() — ボディを dict として受け取る
  • body.get("done", False) — キーがない場合のデフォルト値を指定する

ドリル 2: パスパラメータ + POST + HTTPException - User API を白紙から

ドリル名: 02-build-user-api

> 🛠️ 【タイプ: ゼロベース実装問題】 ほぼ空の main.py から仕様を読んで自力で実装してください。ファイル構造・関数名・内部ロジックは完全に自由。ただしテストが叩く インターフェース (URL / HTTP メソッド / レスポンス形式) は仕様どおりに揃えること。

問題

ユーザー情報を返す API を main.py白紙から 実装してください。

パスパラメータと POST ボディの両方を扱う総合問題です。

仕様

ユーザーデータはコード内で固定 (DB 不要):

USERS = [
    {"id": 1, "name": "Alice", "age": 30},
    {"id": 2, "name": "Bob", "age": 25},
]
メソッドURLリクエストレスポンス
GET/users-USERS の配列
GET/users/{user_id}(パスパラメータ)該当ユーザー (存在しなければ 404)
POST/echo{"text": "..."}{"echoed": "..."} (text をそのまま echoed に入れる)

守る (テストが見ている)

  • user_idint 型として受け取る
  • 存在しない user_id への GET は 404
  • POST /echo は JSON ボディの textechoed キーで返す

自由に設計できる例

  • ユーザー検索ロジック (for / dict / next() 等)
  • Pydantic を使う / 使わない (POST ボディ受け取り)

このドリルで学ぶこと

学習要素学習資料との対応
GET一覧 + GETパスパラメータ + POSTボディ を1つの API に統合ch02 + ch03 の総合
パスパラメータの型ヒント (user_id: int)ch02 slide「仕組み」
HTTPException(status_code=404) で「見つからない」を表現ch02 slide「見つからない時の 404」
Pydantic BaseModel でリクエストボディを受け取るch03 slide「BaseModel を使う完全形」

このドリルは ch02 と ch03 を横断する総合問題。複数の概念を1ファイルに同居させる「設計の統合」を体験します。

ドリル 2: ヒント

ドリル名: 02-build-user-api

> ⚠️ 自力で考えてから読んでください

GET /users

USERS をそのまま return するだけ。

GET /users/{user_id}

  • def get_user(user_id: int): で型ヒントを付ける
  • USERS をループして該当ユーザーを探す
  • 見つからなければ raise HTTPException(status_code=404)

POST /echo

  • Pydantic BaseModeltext: str を持つクラスを定義
  • def echo(payload: EchoPayload): で受け取り
  • {"echoed": payload.text} を返す

考えてみよう

既定では正解・不正解の判定はしません。自分のペースで「答えを見る」を。採点してほしい場合だけ「採点してほしい」を押してください。

ch03-post 演習ガイド

受講生向け演習問題集

採点の仕組み (読んでから始めてください)

採点テストは 「お題の通りに動くか」 を機械的にチェックします。

ドリルごとに 3つのタイプ があるので、各ドリル冒頭の 【タイプ】 を確認してください。

タイプ fill (穴埋め問題) — 文法・APIを知っているか

  • ___ (3個並んだアンダースコア) の空欄を埋める
  • 既存の構造は そのまま
  • ロジック自由度: 低 (お題どおりに直す)

タイプ add (機能追加問題) — 既存コードに機能を足せるか

  • 既存コードを残したまま、新しいエンドポイント/機能を 追加 する
  • 実装方法は自由 (関数名・変数名・内部ロジックは好きに)
  • ただしテストが要求する I/F (URL・HTTPメソッド・レスポンスのキー名) は守る
  • 既存テストを壊さないこと

タイプ build (ゼロベース実装問題) — 仕様から実装を組み立てられるか

  • ほぼ空の main.py から 自力で全部書く
  • ファイル構造・関数分割・命名・内部ロジック: 完全に自由
  • 守るのはインターフェースだけ: URL・HTTP メソッド・レスポンス JSON 形式
  • 一番ハードだが、実装力が一番つくタイプ

3タイプ共通で守ること

  • ファイル名 (main.py) は変えない
  • tests/ フォルダは触らない (採点ロジック)

このガイドのドリル一覧

#ドリル名タイトルタイプ難易度
101-fill-request-body@app.post + Request.json() - リクエストボディ受け取り🔧 穴埋め
202-build-user-apiパスパラメータ + POST + HTTPException - User API を白紙から🛠️ ゼロ実装★★

> 詰まったら ヒント ページを開く。それでも分からなければ講師に質問。

ドリル 1: @app.post + Request.json() - リクエストボディ受け取り

ドリル名: 01-fill-request-body

> 🔧 【タイプ: 穴埋め問題】 ___ の部分を埋めて完成させてください。既存のコード構造は変えないこと。

___ を埋めて、POST エンドポイントを完成させてください。

  • Request を使ってボディを受け取る
  • done が省略された場合は False をデフォルトにする
  • ステータスコードは 201 を返す
from fastapi import FastAPI, ___

app = FastAPI()

@app.post("/tasks", status_code=___)
async def create_task(request: ___):
    body = await request.___()
    new_task = {
        "title": body["title"],
        "done": body.___(  "done", False),
    }
    return new_task

ドリル 1: ヒント

ドリル名: 01-fill-request-body

> ⚠️ 自力で考えてから読んでください

  • from fastapi import FastAPI, RequestRequest もインポートする
  • status_code=201 — 作成成功のステータスコード
  • async def + await request.json() — ボディを dict として受け取る
  • body.get("done", False) — キーがない場合のデフォルト値を指定する

ドリル 2: パスパラメータ + POST + HTTPException - User API を白紙から

ドリル名: 02-build-user-api

> 🛠️ 【タイプ: ゼロベース実装問題】 ほぼ空の main.py から仕様を読んで自力で実装してください。ファイル構造・関数名・内部ロジックは完全に自由。ただしテストが叩く インターフェース (URL / HTTP メソッド / レスポンス形式) は仕様どおりに揃えること。

問題

ユーザー情報を返す API を main.py白紙から 実装してください。

パスパラメータと POST ボディの両方を扱う総合問題です。

仕様

ユーザーデータはコード内で固定 (DB 不要):

USERS = [
    {"id": 1, "name": "Alice", "age": 30},
    {"id": 2, "name": "Bob", "age": 25},
]
メソッドURLリクエストレスポンス
GET/users-USERS の配列
GET/users/{user_id}(パスパラメータ)該当ユーザー (存在しなければ 404)
POST/echo{"text": "..."}{"echoed": "..."} (text をそのまま echoed に入れる)

守る (テストが見ている)

  • user_idint 型として受け取る
  • 存在しない user_id への GET は 404
  • POST /echo は JSON ボディの textechoed キーで返す

自由に設計できる例

  • ユーザー検索ロジック (for / dict / next() 等)
  • Pydantic を使う / 使わない (POST ボディ受け取り)

このドリルで学ぶこと

学習要素学習資料との対応
GET一覧 + GETパスパラメータ + POSTボディ を1つの API に統合ch02 + ch03 の総合
パスパラメータの型ヒント (user_id: int)ch02 slide「仕組み」
HTTPException(status_code=404) で「見つからない」を表現ch02 slide「見つからない時の 404」
Pydantic BaseModel でリクエストボディを受け取るch03 slide「BaseModel を使う完全形」

このドリルは ch02 と ch03 を横断する総合問題。複数の概念を1ファイルに同居させる「設計の統合」を体験します。

ドリル 2: ヒント

ドリル名: 02-build-user-api

> ⚠️ 自力で考えてから読んでください

GET /users

USERS をそのまま return するだけ。

GET /users/{user_id}

  • def get_user(user_id: int): で型ヒントを付ける
  • USERS をループして該当ユーザーを探す
  • 見つからなければ raise HTTPException(status_code=404)

POST /echo

  • Pydantic BaseModeltext: str を持つクラスを定義
  • def echo(payload: EchoPayload): で受け取り
  • {"echoed": payload.text} を返す

ch03-post 演習解説

⚠️ 自力でドリルを解いてから読んでください

ドリル 1: @app.post + Request.json() - リクエストボディ受け取り — 模範解答

ドリル名: 01-fill-request-body

from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/tasks", status_code=201)
async def create_task(request: Request):
    body = await request.json()
    new_task = {
        "title": body["title"],
        "done": body.get("done", False),
    }
    return new_task

ドリル 1: @app.post + Request.json() - リクエストボディ受け取り — 解説

ドリル名: 01-fill-request-body

from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/tasks", status_code=201)
async def create_task(request: Request):
    body = await request.json()
    new_task = {
        "title": body["title"],
        "done": body.get("done", False),
    }
    return new_task

穴埋め一覧

答え理由
import ___ Requestリクエストオブジェクトを受け取るクラス
status_code=___201作成成功を表すHTTPステータス
request: ___Request引数の型アノテーション
request.___()jsonボディをdictとして受け取るメソッド
body.___(...)getキーがない場合のデフォルト値を返すdictメソッド

ドリル 2: パスパラメータ + POST + HTTPException - User API を白紙から — 模範解答

ドリル名: 02-build-user-api

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

USERS = [
    {"id": 1, "name": "Alice", "age": 30},
    {"id": 2, "name": "Bob", "age": 25},
]

class EchoPayload(BaseModel):
    text: str

@app.get("/users")
def list_users():
    return USERS

@app.get("/users/{user_id}")
def get_user(user_id: int):
    for u in USERS:
        if u["id"] == user_id:
            return u
    raise HTTPException(status_code=404, detail="user not found")

@app.post("/echo")
def echo(payload: EchoPayload):
    return {"echoed": payload.text}

ドリル 2: パスパラメータ + POST + HTTPException - User API を白紙から — 解説

ドリル名: 02-build-user-api

ch02-path-params + ch03-post の総合問題。

ポイント

  • 型ヒント (user_id: int) を付けると FastAPI が自動で int に変換、できなければ 422 を返す
  • Pydantic BaseModel で POST ボディを受け取ると、JSON Schema が自動生成 + バリデーション
  • 404 を返す には HTTPException(status_code=404)raise する

実装が複数あり得る点

  • ユーザー検索: for ループ / next((u for u in USERS if u["id"] == id), None) / dict[int, dict] に変換しておく
  • 見つからない時の detail メッセージ: 自由 (テストは status_code しか見ていない)
力試ししたい方は クイズ形式の演習(任意) もあります。読むだけでも十分です。