【Next.js】API Routesを使わなくても、Next.jsを選ぶ理由

こんにちは~Webエンジニアのマッシュルーム🍄です! 

皆さんはNext.jsは使っていますか?
僕は最近までReactはフレームワークだと思っていました...!!
(正確にはJavaScriptのライブラリですよね。jQuery的な )

突然ですが、

バックエンドは別で作るから、Next.jsのAPI Routesは使わない。

じゃあReactだけでいいんじゃ?

こう思ったことはありませんか?

実はNext.jsの魅力はAPI Routesじゃないところにあります!!
この記事では、FastAPIをバックエンドに使うSaaSを想定しながら、「それでもNext.jsを選ぶ理由」を解説します。

目次

  1. 構成の整理
  2. レンダリング方式を自由に選べる
  3. ルーティングがフォルダ構造だけで完結する
  4. Server Componentsでデータ取得がシンプルになる
  5. FastAPIとの連携パターン
  6. まとめ

1. 構成の整理

まず前提を揃えます。今回考えているのはこんな構成です。

サーバー構成

[ブラウザ]
    ↓
[Nginx]
  ├── /      → Next.js  :3000  ← フロントエンド
  └── /api/  → FastAPI  :8000  ← バックエンドAPI

Next.jsのAPI Routesは一切使わず、PythonのフレームワークであるFastAPIがAPIをすべて担当します。

比較対象は「Vite + React」のSPA構成です。どちらも同じFastAPIと組み合わせるとして、フロント側で何が変わるかを見ていきます。

2. レンダリング方式を自由に選べる

純粋なReact(SPA)はすべてブラウザ側でHTMLを組み立てます。ページを開くとまずJavaScriptを読み込み、それが動いてからやっと画面が表示されます。
これをCSR(クライアントサイドレンダリング)といいます。

Next.jsのいいところは、ページごとにレンダリング方式を選べることです。

CSR

ブラウザで描画。ダッシュボードなどSEO不要な動的ページに

SSG

ビルド時にHTMLを生成。ランディングページや更新の少ないページに

SSR(よく使う!)

リクエストのたびにサーバーでHTMLを生成。動的でSEOも効かせたいページに

ISR

SSGをバックグラウンドで定期更新。定期的なHTML再生成が必要なコンテンツに

ポイント

「SEOが必要かどうかまだわからない」という段階では、Next.jsを選んでおくのが安心です。
Vite + ReactのSPAはデフォルトでSSRの仕組みを持たず、後から追加するには構成の大幅な見直しが必要です。Next.jsなら最初からCSRで作って、後でSSRに切り替えるコストが低いです。

3. ルーティングがフォルダ構造だけで完結する

React単体にはルーターが含まれていません。
React Routerなどを別途インストールして、設定ファイルにルートを書く必要があります。

Next.jsでは、app/ フォルダにファイルを置くだけでURLが決まります。

app/ ディレクトリの例

app/
├── page.tsx           # → /
├── layout.tsx         # 全ページ共通のレイアウト
├── dashboard/
│   ├── page.tsx       # → /dashboard
│   └── settings/
│       └── page.tsx   # → /dashboard/settings
└── blog/
    └── [slug]/
        └── page.tsx   # → /blog/なんでも(動的ルート)

layout.tsx を使うと、ネストしたレイアウトも直感的に書けます。
たとえば「ログイン済みユーザー専用のサイドバー付きレイアウトを、ダッシュボード以下のページすべてに当てる」といったことが、設定ファイルなしで実現できます。

4. Server Componentsでデータ取得がシンプルになる

Next.js 13以降のApp Routerには、React Server Components(RSC)という仕組みがあります。難しく聞こえますが、要は「サーバー上だけで動くコンポーネント」です。

Q 何がうれしいの?

A FastAPIからデータを取得して表示するだけなら、useState も useEffect も要りません。
コードがかなりスッキリします。

React SPAだと、データ取得はこんな感じになります。
React SPA (useEffectでデータ取得)

const
[projects, setProjects] = useState([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
     fetch("/api/projects")
          .then(r => r.json())
          .then(data => {
                setProjects(data);
                 setLoading(false);
           });
}, []);

if (loading) return <Spinner />; // ローディング表示が必要
Next.jsのServer Componentを使うとこうなります。
Next.js Server Component(シンプル!!)

// async/awaitで直接書けるし、クライアントに送られない
export default async function DashboardPage() {

    const res = await fetch("http://localhost:8000/api/projects");
    const projects = await res.json();
    return (
        <ul> {
            projects.map(p => <li key={p.id}>{p.name}</li>)}
        </ul>
    );
}

ローディング状態の管理も不要で、初期表示からHTMLにデータが含まれるのでSEOにも有利です。

5. FastAPIとの連携パターン

認証のリダイレクトを一か所にまとめる

Next.jsには middleware.ts という仕組みがあります。
「ログインしていない人が /dashboard にアクセスしたらログイン画面に飛ばす」という処理を、ひとつのファイルにまとめて書けます。

//  middleware.ts(認証チェックの例)
export function
middleware(req) {
     const token = req.cookies.get("access_token"); // トークンなし → ログイン画面へリダイレクト
      
if (!token && req.nextUrl.pathname.startsWith("/dashboard")) {
           return NextResponse.redirect(new URL("/login", req.url));
     }
}
export const config = { matcher: ["/dashboard/:path*"] };
環境変数でAPIのURLを切り替える
.env.local
# サーバー側だけで使う(ブラウザには届かない)
FASTAPI_INTERNAL_URL=http://localhost:8000

# ブラウザ側でも使えるURL(NEXT_PUBLIC_ が必要)
NEXT_PUBLIC_API_URL=https://api.example.com
注意

NEXT_PUBLIC_ をつけると、その変数はJavaScriptバンドルに含まれてブラウザから見えます。
APIキーやシークレット情報には絶対につけないでください。

6. まとめ

「API Routesを使わないならNext.jsの意味がない」は誤解です。Next.jsのメリットはレンダリングの柔軟性、ルーティングの簡潔さ、Server Componentsによるシンプルなデータ取得など、フロントエンドの設計全体にわたっています。

▼Next.jsを選ぶと得られるもの
  • ページごとにCSR / SSR / SSG / ISRを使い分けられる

  • フォルダ構造だけでルーティングが完結する

  • Server Componentsでデータ取得コードがスッキリする

  • ミドルウェアで認証処理を一元管理できる

  • 後からSEOを強化したくなっても対応できる

もちろん、Next.jsはVite + Reactよりも覚えることが多いのは事実です。社内ツールや管理画面でSEOが完全に不要なら、シンプルなVite構成の方が向いているケースもあります。ただ、SaaSとして育てることを見据えているなら、Next.jsを選んでおいて後悔することはないと思います。

 それでは、また次回の記事でお会いしましょう~🍄