Next.jsとFastAPIの連携設計:SPAのAPI、型共有、デプロイを実務解説

black laptop computer turned on showing computer codes
Photo by Markus Spiske on Pexels.com

Next.jsはReactを基盤に、画面のルーティングやレンダリング、配信を担うWebフレームワークです。
FastAPIはPythonの型注釈とPydanticモデルを利用して、Web APIの入力検証や出力定義を組み立てるフレームワークです。

両者を組み合わせるときは、技術を接続する前に「どこで画面を描画するか」「ブラウザとAPIをどの経路でつなぐか」「どちらを独立して配備するか」を決めます。
この三点が曖昧なまま実装すると、CORS、認証、キャッシュ、環境変数の扱いが後から食い違います。

この記事では、Next.jsとFastAPIの役割分担から、API契約、データ取得、配信方式、運用確認までを一つの設計手順として整理します。

Next.jsとFastAPIの役割を分ける

SPA(Single-Page Application)は、画面遷移のたびに文書全体を再読み込みせず、ブラウザ側のJavaScriptが表示を更新するWebアプリケーションです。
ただし、Next.jsを採用したからといって、すべての画面をブラウザだけで描画する必要はありません。

Next.jsは、クライアント側の滑らかな画面遷移に加えて、静的生成やサーバー側のレンダリングも選べます。
そのため、ここで扱う「SPA」は画面遷移の体験を指し、配信方式まで一つに限定する言葉ではありません。
Next.jsの位置付けをほかの技術と比較したい場合は、Vue.js、Nuxt.js、React、Next.jsの役割の違いも参考になります。

領域 Next.jsの主な役割 FastAPIの主な役割
画面 ルーティング、表示、入力操作、メタデータ 画面が必要とするデータの提供
データ 取得タイミングと表示状態の管理 入力検証、業務処理、レスポンス定義
実行環境 Node.jsサーバー、管理サービス、静的書き出し ASGIサーバー、コンテナ、クラウド実行環境
運用 画面エラー、表示速度、キャッシュの監視 APIエラー、処理時間、依存サービスの監視

配信方式を先に決める

構成を選ぶ基準は、単に「サーバーを一つにしたいか」ではありません。
Next.jsのサーバー機能が必要か、フロントエンドとAPIを別々に更新したいか、ブラウザから見たオリジンをそろえるかで判断します。

方式 構成 向いている状況 注意点
別々に配備 Next.jsとFastAPIを別サービスで実行 個別に更新、監視、拡張したい ブラウザが別オリジンへ接続する場合はCORS設定が必要
同一ドメインで中継 リバースプロキシが画面と/apiを振り分ける 利用者向けURLを一つにまとめたい プロキシ設定、転送ヘッダー、タイムアウトも運用対象になる
静的書き出し Next.jsのout/を静的配信し、FastAPIをAPIとして実行 実行時のNext.jsサーバー機能が不要 サーバー機能を必要とするページや処理は利用できない

Next.jsをサーバーとして動かす構成では、FastAPIから.next/をそのまま公開するだけでは、サーバー側レンダリングや実行時の機能を代替できません。
静的ファイルとしてまとめたい場合は、静的書き出しを選び、ビルドで生成されたout/を配信します。

変更範囲が分かるプロジェクト構成

フロントエンドとバックエンドは、依存関係と配備単位が異なります。
同じリポジトリで管理する場合も、実装、テスト、コンテナ定義を分けると、変更の影響を追いやすくなります。

my-project/
├── frontend/              # Next.js
│   ├── app/
│   ├── src/lib/api/       # API呼び出しと生成した型
│   └── tests/
├── backend/               # FastAPI
│   ├── app/
│   │   ├── main.py
│   │   ├── routers/
│   │   ├── schemas/
│   │   └── services/
│   └── tests/
├── compose.yaml
└── README.md

ルーターはHTTPの入口、スキーマは入出力の形、サービスは業務処理を置く場所です。
この分け方は絶対的な規則ではありませんが、ルーターに検証、データ操作、外部通信を詰め込む構成より、テスト対象を切り分けやすくなります。

小規模な段階から過剰に細分化する必要はありません。
最初はmain.pyと一つのルーターで始め、責務が重なった時点でスキーマやサービスを分ける方法も取れます。

OpenAPIをAPI契約として使う

API契約とは、URL、HTTPメソッド、入力項目、出力項目、エラーの形について、フロントエンドとバックエンドが共有する取り決めです。
FastAPIではPydanticモデルからJSON Schemaを作り、その定義をOpenAPI文書に含められます。

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class UserOut(BaseModel):
    id: int
    name: str

@app.get("/api/users/{user_id}", response_model=UserOut)
async def get_user(user_id: int) -> UserOut:
    return UserOut(id=user_id, name="Sample User")

Next.js側では、FastAPIのOpenAPI文書からTypeScript型やAPIクライアントを生成できます。
手書きの型を二重管理するより変更差分を見つけやすくなりますが、型を生成しただけで実行時のレスポンスが保証されるわけではありません。

次の簡略例は、同一オリジンの/apiをFastAPIへ転送し、生成した型を利用する構成を想定しています。

type User = {
  id: number;
  name: string;
};

export async function getUser(userId: number): Promise<User> {
  const response = await fetch(`/api/users/${userId}`);

  if (!response.ok) {
    throw new Error(`User API failed: ${response.status}`);
  }

  return (await response.json()) as User;
}

このas UserはTypeScriptコンパイラへの宣言であり、受信したJSONを検証する処理ではありません。
FastAPIの出力モデル、契約テスト、必要に応じたフロントエンド側の実行時検証を組み合わせて、仕様と実データのずれを検出します。
入力検証や出力制御まで詳しく確認したい場合は、FastAPIでAPI契約を保つための型定義とテストを参照してください。

データを取得する場所を選ぶ

データ取得は、画面ごとに「サーバーで取得するか」「ブラウザで取得するか」を決めます。
検索結果に表示したい初期内容や、表示前に必要なデータはサーバーで取得し、ブラウザAPIに依存する処理や頻繁な再取得はクライアント側に置くと役割が明確になります。

Pages RouterのgetServerSidePropsは、App Routerのデータ取得方法ではありません。
App Routerでは非同期のServer Componentからデータソースを呼び出せるため、ルーターの種類を決めてから実装例を選びます。

  • Server Component:初期表示に必要なデータをサーバーで取得し、認証情報や内部URLをブラウザへ出したくない場合に使います。
  • Client Component:ユーザー操作後の再取得、ブラウザAPI、短い間隔で更新する表示に使います。
  • Next.jsの中継処理:ブラウザとFastAPIの間でCookieや公開項目を調整する必要がある場合に検討します。

Server Componentから自サイトの中継URLを経由してFastAPIを呼ぶと、不要なHTTP往復が一回増える場合があります。
中継が担う処理がなければ、サーバーからFastAPIを直接呼ぶ構成のほうが単純です。

CORSと認証を混同しない

オリジンは、URLのスキーム、ホスト、ポートの組み合わせです。
たとえばhttp://localhost:3000とhttp://localhost:8000はポートが異なるため、別オリジンとして扱われます。

ブラウザから別オリジンのFastAPIへ接続する場合は、許可するフロントエンドのオリジンを明示します。
CookieやAuthorizationヘッダーを使う構成では、無条件のワイルドカード許可を避け、環境ごとのURLを設定します。

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://app.example.com"],
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
)

CORSは、ブラウザが別オリジンへ送るリクエストを制御する仕組みです。
利用者の本人確認、操作権限、CSRFへの対策を代わりに行うものではないため、認証と認可はAPI側で別に設計します。

デプロイと運用の確認項目

コンテナ化は実行環境をそろえる手段であり、それだけで本番運用が完成するわけではありません。
Next.jsとFastAPIを別コンテナにする場合は、公開経路、HTTPS、再起動、ログ、ヘルスチェック、データベース更新の順序まで決めます。

  • 開発、検証、本番でAPIのベースURLと許可オリジンを分ける
  • 秘密情報をブラウザ向けの環境変数へ含めない
  • OpenAPI文書の差分を確認し、互換性を壊す変更を検知する
  • フロントエンドとAPIのヘルスチェックを分ける
  • HTTPステータス、処理時間、例外を追えるログを残す
  • リリース後に主要画面と主要APIを確認する

Dockerfile、Compose、Pythonの依存関係の役割から確認したい場合は、PythonアプリのDocker構成と各ファイルの役割から読むと構成を追いやすくなります。

画面実装のアクセシビリティ

アクセシビリティは記事の説明方法ではなく、作る画面の設計と実装で確認します。
Next.jsとFastAPIの構成でも、キーボード操作、フォーカス移動、フォームのラベル、エラー通知、読み込み中の状態を画面要件に含めます。

  • 見出し、ナビゲーション、ボタン、フォームを役割に合うHTML要素で実装する
  • キーボードだけで操作でき、現在のフォーカスを見分けられるようにする
  • 入力エラーは色だけで示さず、対象項目と修正方法を文章で伝える
  • APIの待機中、成功、データなし、失敗をそれぞれ区別して表示する
  • 非同期更新のあとに、読み上げ利用者が変化を把握できる通知方法を選ぶ

APIが詳細なエラー情報を返しても、そのまま画面へ表示すると利用者には分かりにくく、内部情報を見せるおそれもあります。
画面では次に取れる行動を示し、開発者向けの詳細はログで追えるように分けます。

実装を進める順序

  1. App RouterかPages Routerかを決め、必要なレンダリング方式を整理する
  2. Next.jsとFastAPIを別配備にするか、同一ドメインで中継するかを決める
  3. Pydanticモデルで最小のAPI契約を作り、OpenAPI文書を確認する
  4. Next.jsから一つのAPIを呼び、成功、データなし、失敗の画面を作る
  5. 認証、認可、CORS、タイムアウトを本番の経路に合わせて設定する
  6. 契約テストと主要画面のテストを自動化し、監視を用意して配備する

最初からすべての仕組みを入れるのではなく、一本のAPIを本番と同じ経路で通すと、役割分担の誤りを早い段階で見つけられます。
その後にエンドポイントと画面を増やせば、同じ設計判断を再利用できます。

設計判断の要点

  • Next.jsは画面とレンダリング、FastAPIはAPI契約と業務処理を中心に責務を分ける
  • App RouterとPages Routerのデータ取得方法を混在させない
  • OpenAPIから型を共有し、出力モデルとテストで実データのずれも確認する
  • Next.jsのサーバー機能を使う構成と、out/を配る静的書き出しを区別する
  • CORS、認証、運用監視、アクセシビリティをリリース前の設計項目に含める

Next.jsとFastAPIの組み合わせを保守しやすくするのは、フレームワークの数ではなく、画面、API、配信、運用の境界をチームで共有できる設計です。

この記事に関連する株式会社greedenの取り組み

Next.jsとFastAPIの連携では、画面とAPIの役割、型の整合、配信方式を初期設計で決めることが保守の土台になります。株式会社greedenは、要件定義から実装、テスト、保守改善まで、Webシステム開発を一気通貫で支援します。

投稿者 greeden

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)