Azure Static Web Apps に Next.js ハイブリッド サイトをデプロイする (プレビュー)

このチュートリアルでは、React サーバー コンポーネント、サーバー側レンダリング (SSR)、API ルートなどの Next.js の機能のサポートを使って、Next.js の Web サイトを Azure Static Web Apps にデプロイする方法について説明します。

Note

Next.js ハイブリッドのサポートはプレビュー段階です。

前提条件

プレビューでサポートされていない機能

Static Web Apps の次の機能は、ハイブリッド レンダリングを使用する Next.js ではサポートされていません。

  • Azure Functions、Azure App Service、Azure Container Apps、または Azure API Management を使用するリンクされた API。
  • SWA CLI のローカル エミュレーションとデプロイ。
  • staticwebapp.config.json ファイルの部分的なサポート。
    • ナビゲーション フォールバックはサポートされていません。
    • Next.js アプリケーション内のルートへのルートの書き換えは、next.config.js 内で構成する必要があります。
    • staticwebapp.config.json ファイル内の構成は、next.config.js 内の構成より優先されます。
    • 機能の完全な互換性のためには、next.config.js を使って Next.js サイトの構成を処理する必要があります。
  • skip_app_buildskip_api_build は、Azure/static-web-apps-deploy@v1 デプロイ イメージ内では使用できません。
  • 増分静的再生成 (ISR) では、イメージのキャッシュはサポートされていません。

Note

ハイブリッド Next.js アプリケーションの最大アプリ サイズは 250 MB です。 最適化されたアプリ サイズには、Next.js によるスタンドアロン機能を使用します。 これでも十分でない場合、アプリ サイズの要件が 250 MB を超える場合は、静的 HTML エクスポート済み Next.js の使用を検討してください。

リポジトリを作成する

この記事では、簡単に作業を開始できるように、GitHub テンプレート リポジトリを使用します。 テンプレートには、Azure Static Web Apps にデプロイするスターター アプリが含まれます。

  1. 次の場所に移動して、新しいリポジトリを作成します。

    https://github.com/staticwebdev/nextjs-hybrid-starter/generate

  2. リポジトリの名前を my-first-static-web-app に設定します

  3. [Create repository from template](テンプレートからリポジトリを作成する) を選択します。

    [テンプレートからリポジトリを作成する] ボタンのスクリーンショット。

静的 Web アプリを作成する

リポジトリが作成されたので、Azure portal から静的 Web アプリを作成できます。

  1. Azure ポータルにアクセスします。
  2. [リソースの作成] を選択します。
  3. Static Web Apps を検索します。
  4. Static Web Apps を選択します。
  5. [作成] を選択します

[基本] セクションで、新しいアプリを構成し、それを GitHub リポジトリにリンクすることから始めます。

Azure portal の [基本] セクションのスクリーンショット。

設定
サブスクリプション Azure サブスクリプションを選択します。
リソース グループ [新規作成] リンクを選択し、テキスト ボックスに「static-web-apps-test」と入力します。
名前 テキスト ボックスに、「my-first-static-web-app」と入力します。
プランの種類 [無料] を選択します。
Azure Functions and staging details (Azure Functions とステージングの詳細) 最も近いリージョンを選択します。
source [GitHub] を選択します。

[GitHub アカウントでサインイン] を選び、GitHub で認証します。

GitHub にサインインした後、リポジトリ情報を入力します。

設定
Organization 自分の組織を選択します。
リポジトリ [my-first-web-static-app] を選択します。
[Branch]\(ブランチ) [main](メイン) を選択します。

Azure portal のリポジトリの詳細のスクリーンショット。

Note

リポジトリが表示されない場合、次の手順を実行します。

  • GitHub で Azure Static Web Apps を承認する必要がある場合があります。 GitHub リポジトリを参照し、[設定] > [アプリケーション] > [認可された OAuth アプリ] の順に移動して、[Azure Static Web Apps][許可] の順に選択します。
  • Azure DevOps 組織で Azure Static Web Apps を承認する必要がある場合があります。 アクセス許可を付与する組織の所有者である必要があります。 OAuth を使用してサード パーティ アプリケーションのアクセスを要求します。 詳細については、OAuth 2.0 を使用した REST API へのアクセスの承認に関する記事を参照してください。

[ビルドの詳細] セクションで、使用するフロントエンド フレームワークに固有の構成の詳細を追加します。

  1. [ビルドのプリセット] ドロップダウンから [Next.js] を選びます。

  2. [App location](アプリの場所) ボックスは既定値のままにします。

  3. [Api location](API の場所) ボックスは空のままにします。

  4. [App artifact location]\(アプリ成果物の場所\) ボックスは空白のままにします。

[Review + create](レビュー + 作成) を選択します。

[作成] ボタンのスクリーンショット。

Web サイトを表示する

静的アプリのデプロイには 2 つの側面があります。 1 つ目は、アプリを構成する基になる Azure リソースを作成することです。 2 つ目は、アプリケーションをビルドして発行するワークフローです。

新しい静的サイトに移動する前にまず、デプロイ ビルドの実行が完了している必要があります。

Static Web Apps の "概要" ウィンドウには、Web アプリとの対話に役立つ一連のリンクが表示されます。

Azure Static Web Apps の概要ウィンドウのスクリーンショット。

"Select here to check the status of your GitHub Actions runs (こちらを選択して、GitHub Actions の実行の状態を確認してください)" というバナーを選択すると、リポジトリに対して実行されている GitHub アクションが表示されます。 デプロイ ジョブが完了したことを確認したら、生成された URL を使用して Web サイトに移動できます。

GitHub Actions ワークフローが完了したら、 [URL] リンクを選択して、新しいタブで Web サイトを開くことができます。

変更を行うためにローカル環境に Next.js プロジェクトを設定する

  1. 新しいリポジトリをお使いのコンピューターに複製します。 <YOUR_GITHUB_ACCOUNT_NAME> は、必ず自分のアカウント名に置き換えてください。

    git clone http://github.com/<YOUR_GITHUB_ACCOUNT_NAME>/my-first-static-web-app
    
  2. Visual Studio Code または任意のコード エディターでプロジェクトを開きます。

サーバー コンポーネントを使用してサーバー レンダリング済みデータを追加する

アプリ ルーターを使って Next.js プロジェクトにサーバー レンダリング済みデータを追加するには、Next.js コンポーネントを編集して、データをレンダリングするためのサーバー側操作をコンポーネントに追加します。 既定では、Next.js コンポーネントはサーバー レンダリング可能なサーバー コンポーネントです。

  1. app/page.tsx ファイルを開き、サーバー側で計算される変数の値を設定する操作を追加します。 たとえば、データのフェッチやその他のサーバー操作などです。

    export default function Home() {
        const timeOnServer = new Date().toLocaleTimeString('en-US');
        return(
            ...
        );
    }
    
  2. next/cache から unstable_noStore をインポートし、Home コンポーネント内でそれを呼び出して、ルートが動的にレンダリングされるようにします。

    import { unstable_noStore as noStore } from 'next/cache';
    
    export default function Home() {
        noStore();
    
        const timeOnServer = new Date().toLocaleTimeString('en-US');
        return(
            ...
        );
    }
    

    Note

    この例では、サーバーの現在時刻のサーバー レンダリングを見ていただくために、このコンポーネントを強制的に動的レンダリングします。 Next.js のアプリ ルーター モデルでは、Next.js アプリのパフォーマンスを最適にするため、個々のデータ要求をキャッシュすることをお勧めします。 詳しくは、Next.js でのデータのフェッチとキャッシュに関するページをご覧ください。

  3. サーバー側のデータをレンダリングするように、app/pages.tsxHome コンポーネントを更新します。

    import { unstable_noStore as noStore } from 'next/cache';
    
    export default function Home() {
        noStore();
    
        const timeOnServer = new Date().toLocaleTimeString('en-US');
        return(
            <main className="flex min-h-screen flex-col items-center justify-between p-24">
                <div>
                    This is a Next.js application hosted on Azure Static Web Apps with 
                    hybrid rendering. The time on the server is <strong>{timeOnServer}</strong>.
                </div>
            </main>
        );
    }
    

API ルートの追加

Next.js には、サーバー コンポーネントに加えて、Next.js アプリケーションへの API ルートの作成に使用できるルート ハンドラーが用意されています。 これらの API は、クライアント コンポーネントでフェッチできます。

まず API ルートを追加します。

  1. app/api/currentTime/route.tsx に新しいファイルを作成します。 このファイルは、新しい API エンドポイントのためのルート ハンドラーを保持します。

  2. API からデータを返すハンドラー関数を追加します。

    import { NextResponse } from 'next/server';
    
    export const dynamic = 'force-dynamic';
    
    export async function GET() { 
        const currentTime = new Date().toLocaleTimeString('en-US');
    
        return NextResponse.json({ 
            message: `Hello from the API! The current time is ${currentTime}.`
        });
    }
    
  3. app/components/CurrentTimeFromAPI.tsx に新しいファイルを作成します。 このコンポーネントは、ブラウザーから API をフェッチするクライアント コンポーネントのコンテナーを作成します。

  4. API をフェッチするクライアント コンポーネントをこのファイルに追加します。

    'use client';
    
    import { useEffect, useState } from 'react';
    
    export function CurrentTimeFromAPI(){
        const [apiResponse, setApiResponse] = useState('');
        const [loading, setLoading] = useState(true);
    
        useEffect(() => {
            fetch('/api/currentTime')
                .then((res) => res.json())
                .then((data) => {
                setApiResponse(data.message);
                setLoading(false);
                });
            }, 
        []);
    
        return (
            <div className='pt-4'>
                The message from the API is: <strong>{apiResponse}</strong>
            </div>
        )
    }
    

このクライアント コンポーネントは、読み込みが完了した後でコンポーネントをレンダリングするために、useEffect React フックを使って API をフェッチします。 'use client' ディレクティブは、この要素をクライアント コンポーネントとして識別します。 詳しくは、「クライアント コンポーネント」をご覧ください。

  1. CurrentTimeFromAPI クライアント コンポーネントをインポートしてレンダリングするように、app/page.tsx を編集します。

    import { unstable_noStore as noStore } from 'next/cache';
    import { CurrentTimeFromAPI } from './components/CurrentTimeFromAPI';
    
    export default function Home() {
        noStore();
    
        const timeOnServer = new Date().toLocaleTimeString('en-US');
        return(
            <main className="flex min-h-screen flex-col items-center justify-between p-24">
                <div>
                    This is a Next.js application hosted on Azure Static Web Apps with 
                    hybrid rendering. The time on the server is <strong>{timeOnServer}</strong>.
                </div>
                <CurrentTimeFromAPI />
            </main>
        );
    }
    
  2. API ルートからの結果がページに表示されます。

API ルートからの出力の表示を示すスクリーンショット。

Next.js のランタイム バージョンを構成する

Next.js の特定のバージョンでは、Node.js の特定のバージョンが必要です。 Node の特定のバージョンを構成するには、package.json ファイルの "engines" プロパティを設定してバージョンを指定できます。

{
  ...
  "engines": {
    "node": "18.17.1"
  }
}

Next.js 用の環境変数を設定する

Next.js は、ビルド時と要求時に環境変数を使って、サーバー側レンダリングで静的ページ生成と動的ページ生成の両方をサポートします。 そのため、ビルド タスクとデプロイ タスク内、および Azure Static Web Apps リソースの [環境変数] 内の両方で、環境変数を設定します。

...
      - name: Build And Deploy
        id: builddeploy
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
          repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
          action: "upload"
          app_location: "/" 
          api_location: ""
          output_location: "" 
        env:
          DB_HOST: ${{ secrets.DB_HOST }}
          DB_USER: ${{ secrets.DB_USER }}
          DB_DATABASE: ${{ secrets.DB_DATABASE }}
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
          DB_PORT: ${{ secrets.DB_PORT }}
...

スタンドアロン機能を有効にする

アプリケーションのサイズが 250 MB を超える場合は、Next.js の出力ファイル トレース機能を使うと、アプリのサイズを最適化し、パフォーマンスを向上させることができます。

出力ファイル トレースでは、アプリケーション全体の圧縮バージョンが作成され、必要なパッケージ依存関係が .next/standalone という名前のフォルダーに組み込まれます。 このフォルダーは、node_modules の依存関係を追加せずに単独でデプロイするためのものです。

next.config.js 機能を有効にするには、次のプロパティを standalone に追加します。

module.exports ={
    output:"standalone",
}

また、静的ファイルをスタンドアロン出力にコピーするには、package.json ファイルで build コマンドを構成する必要もあります。

{
  ...
  "scripts": {
    ...
    "build": "next build && cp -r .next/static .next/standalone/.next/ && cp -r public .next/standalone/"
    ...
  }
  ...
}

Azure Static Web Apps にデプロイするための Next.js ルーティングとミドルウェアを構成する

Next.js プロジェクトは、リダイレクト、書き換え、ミドルウェアを使用してルートをカスタム処理するように構成できます。 これらのハンドラーは、認証、パーソナル化、ルーティング、国際化に一般的に使用されます。 カスタム処理は Next.js サイトの既定のルーティングに影響を与えるため、構成は Static Web Apps でのホスティングと互換性がある必要があります。

Static Web Apps は、ビルド時にサイトにページを追加して、Next.js サイトが正常にデプロイされていることを確認します。 このページは public/.swa/health.html という名前です。Static Web Apps は、/.swa/health.html に移動して応答の成功を確認することにより、サイトが正常に起動され、デプロイされたことを確認します。 リダイレクトと書き換えを含むミドルウェアとカスタム ルーティングは、/.swa/health.html パスのアクセスに影響を与える可能性があり、Static Web Apps のデプロイの検証が妨げられるおそれがあります。 Static Web Apps へのデプロイが成功するようにミドルウェアとルーティングを構成するには、次の手順に従います。

  1. ミドルウェアの構成では、middleware.ts (または .js) ファイルで、.swa で始まるルートを除外します。

    export const config = {
      matcher: [
        /*
         * Match all request paths except for the ones starting with:
         * - .swa (Azure Static Web Apps)
         */
        '/((?!.swa).*)',
      ],
    }
    
  2. next.config.js で、.swa で始まるルートを除外するようにリダイレクトを構成します

    module.exports = {
        async redirects() {
            return [
              {
                source: '/((?!.swa).*)<YOUR MATCHING RULE>',
                destination: '<YOUR REDIRECT RULE>', 
                permanent: false,
              },
            ]
        },
    };
    
  3. next.config.js で、.swa で始まるルートを除外するように書き換えを構成します

    module.exports = {
        async rewrites() {
            return {
                beforeFiles: [
                    {
                        source: '/((?!.swa).*)<YOUR MATCHING RULE>',
                        destination: '<YOUR REWRITE RULE>', 
                    }
                ]
            }
        },
    };
    

これらのコード スニペットは、.swa で始まるパスをカスタム ルーティングまたはミドルウェアによる処理から除外します。 これらの規則により、デプロイの検証中にパスが想定どおりに解決されることが保証されます。

Next.js のログ記録を有効にする

Next.js サーバー API のトラブルシューティングのベスト プラクティスに従って、ログ記録を API に追加し、これらのエラーをキャッチします。 Azure でのログ記録では、Application Insights が使用されます。 この SDK を事前に読み込むには、カスタムのスタートアップ スクリプトを作成する必要があります。 詳細については、以下を参照してください。

リソースをクリーンアップする

このアプリケーションを引き続き使用しない場合は、次の手順を使用して Azure Static Web Apps インスタンスを削除することができます。

  1. Azure portal を開きます。
  2. 上部の検索バーから my-first-web-static-app を検索します。
  3. アプリの名前を選択します。
  4. [削除] を選択します。
  5. [はい] を選択して削除アクションを確定します (このアクションが完了するまでにしばらく時間がかかる場合があります)。

次のステップ