UI(page.tsx
)→ route.ts
→ Django(views.py
) → route.ts
→ UI というリクエスト往復の流れ
前提・構成
- フロント:Next.js App Router(
app/
配下) - API 入口:
app/api/summarize/route.ts
- バックエンド:Django(
/summarizer/summarize/
にviews.py
でエンドポイント) - 通信:UI は 常に Next.js の API Route(同一オリジン
/api/...
)に向け、route.ts
が Django に“中継”する設計
全体のシーケンス
-
route.ts
は プロキシ(ゲートウェイ) として振る舞う。 - セキュリティ(バックエンドURLの秘匿)、エラーフォーマットの統一、将来の差し替えに強い。
route.ts
(実コード+行ごとの注釈)
import { NextResponse } from "next/server";
// Next.js(App Router)のレスポンス生成ユーティリティ
export async function POST(req: Request) {
// この API ルートが受け取る POST メソッドのハンドラ
const { text } = await req.json();
// フロントから届いた JSON 本文をパースし、text プロパティだけを分割代入
try {
const res = await fetch("http://localhost:8000/summarizer/summarize/", {
method: "POST", // サーバーに JSON を送るので POST を明示
headers: { "Content-Type": "application/json" }, // 本文が JSON であることを宣言
body: JSON.stringify({ text }), // JSオブジェクト → JSON文字列へ変換して送信
});
if (!res.ok) {
const errorText = await res.text(); // HTML等のエラーボディをテキストで取得
return NextResponse.json( // フロント向けに「常にJSON」で返す
{ error: errorText },
{ status: res.status }
);
}
const data = await res.json(); // Django からの JSON をオブジェクト化
return NextResponse.json({ summary: data.summary });
// 必要なフィールドだけに整形して返し直す(境界で正規化)
} catch (err) {
return NextResponse.json({ error: String(err) }, { status: 500 });
// 例外は JSON で統一して返す(UI 側の扱いが簡単になる)
}
}
ポイント
-
「二重にJSON化している」わけではない。
- Django の
JsonResponse
は DJ → route.ts の HTTP レスポンス。 -
NextResponse.json(...)
は route.ts → ブラウザ への新しい HTTP レスポンス。 - 異なるレイヤの責務なので、route.ts で“返し直す”のは妥当である。
- Django の
-
ここで フィールドを絞る・ステータスやヘッダを調整 できるのが利点。
UI 側(page.tsx
)の最低限
// 送信: body は必ず JSON.stringify する・POST を明示する・Content-Type を付ける
await fetch("/api/summarize", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text }),
});
// 受信: JSON を取り出して描画
const data = await res.json(); // => { summary: "..." }
setSummary(data.summary);
-
JSON.stringify
を忘れると、[object Object]
などの無効な本文になり、サーバー側で JSON として読めず失敗する。 -
method
を省くとデフォルトは GET であり、body を付けても意味をなさないので 必ず POST を指定する。
バックエンド(Django views.py
)の最小実装
from django.http import JsonResponse, HttpResponseNotAllowed
from django.views.decorators.csrf import csrf_exempt
import json
from .news_summarizer_model import run_summary
@csrf_exempt
def summarize(request):
if request.method == "POST":
data = json.loads(request.body) # Content-Type: application/json 前提で受信
text = data.get("text")
summary = run_summary(text) # 要約処理
return JsonResponse({"summary": summary}) # JSONで返却
return HttpResponseNotAllowed(["POST"], "Use POST method instead")
- App Router から別オリジンに飛ばす設計では、簡易対応として
@csrf_exempt
を付けることが多い(本番運用ではCSRF/CORSや認証を検討する)。
分割代入(const { text } = await req.json()
)の意味
-
await req.json()
は{ text: "..." }
という JSオブジェクトを返す。 - 分割代入で
text
だけを取り出すと、以後data.text
と書かずに済む。 - どのプロパティを使うのかが明示でき、可読性・保守性が上がる。
// 複数取りたい場合
const { text, mode = "medium", lang } = await req.json();
なぜ Content-Type: application/json
が必須か
- サーバーが 本文の形式を判別するためである。
- これがないと Django 側は JSON と認識できず、
json.loads
や DRF の JSON パーサーが正しく動かない。 - フォームなら
application/x-www-form-urlencoded
、ファイルならmultipart/form-data
を指定するのと同じ話である。
よくある落とし穴
-
method
を書かない → デフォルトは GET。body を付けても無効。 -
JSON.stringify
を忘れる → サーバーには"[object Object]"
が届いて JSON として読めない。 -
バックエンドの生エラーページ(HTML) → そのまま返すと UI 側で扱いにくい。
route.ts
で JSON に整形して返すと良い。 - CORS/CSRF → 開発中は緩めても、本番では設計が必要(API ゲートウェイ方式はここを隠蔽しやすい)。
発展:環境変数化と“パススルー”実装
バックエンドURLを環境変数化(例)
const BACKEND_URL = process.env.NEXT_PUBLIC_API_BASE ?? "http://localhost:8000";
const res = await fetch(`${BACKEND_URL}/summarizer/summarize/`, { ... });
- ビルド時に環境ごと(開発・本番)で切替可能になる。
ほぼ“そのまま返す”パススルー例
const res = await fetch(`${BACKEND_URL}/summarizer/summarize/`, { method: "POST", headers, body });
if (!res.ok) {
const text = await res.text();
return NextResponse.json({ error: text }, { status: res.status });
}
const data = await res.json(); // 形を変えず UI へ
return NextResponse.json(data, { status: res.status });
- 今回のように フィールドを最小化したいなら
{ summary: data.summary }
に整形して返すのが明快である。
まとめチェックリスト
-
UI →
fetch
はmethod: "POST"
明示・Content-Type: "application/json"
・JSON.stringify
を必ず付ける -
route.ts
は バックエンドへの中継点として、エラー整形とレスポンス正規化を行う -
NextResponse.json(...)
は フロント向けの新しいHTTPレスポンスを作る処理(“二重JSON化”ではない) -
Django は
JsonResponse
で返す(App Router 側で JSON として受けやすい) - バックエンドURLは 環境変数化、将来の差し替えに備える
-
(将来)認証・CORS/CSRF 方針は
route.ts
境界で統一して扱うと管理が楽になる