はじめに

前回に引き続きAzure上でWebアプリを公開するための新しいサービスである「Azure Static Web Apps」について紹介します。今回は、前回作成したAzure Static Web Appsのアプリに変更を加えて、Azure Functionsと連携してAPIの呼び出しが行えるようにしていきます。また、実際の開発や運用での現場に近いワークフローを実現することのできるステージング環境へのアプリの公開方法についても紹介します。なお、Azure Static Web Appsは執筆時点ではプレビュー版となっています。プレビュー期間は無料で利用することができます。

Azure Static Web AppsとAzure Functionsを使って静的サイトとAPIを連携しよう

前回作成した静的コンテンツを表示するWebサイトに、Azure FunctionsによるAPIを追加してみましょう。API呼び出しを追加することで、より現実的なWebサイトの構成をAzure Static Web Appsで実現できることを確認していきます。

Azure FunctionsによるAPIの実装

Azure Functionsのルールに則った実装方法でAPIを追加していきます。Visual Studio Codeに戻り、まずはAzure Functions用の拡張機能を追加します。Visual Studio Codeの左側メニューから「Extensions」を選択し、Azure Functionsの拡張機能を検索して「Install」します。

  • Azure Functions拡張機能の追加

    Azure Functions拡張機能の追加

Azure Functionsの拡張機能がインストールされたら、ファイルエクスプローラーを開きプロジェクトのルートディレクトリ直下に「api」というディレクトリを新規作成します。この状態でコマンドパレットを開き、「azure functions create new」と入力して「Azure Functions: Create New Project...」を選択します。

  • Azure Functionsプロジェクトの作成

    Azure Functionsプロジェクトの作成

Azure Functionsのプロジェクトを作成するディレクトリを求められるので、「Browse...」を選択して先程作成した「api」ディレクトリを選択します。

  • Azure Functionsプロジェクトを作成するディレクトリの選択

    Azure Functionsプロジェクトを作成するディレクトリの選択

次にAPIを実装する際のプログラミング言語の種類を選択します。今回はJavaScriptを選択します。

  • Azure Functionsの実装言語の選択

    Azure Functionsの実装言語の選択

APIのトリガーを選択します。Azure Functionsでは、APIが実行される契機(トリガー)となるイベントを指定する必要があります。Azure Functionsでサポートされているトリガーには、GETやPOSTなどのHTTPリクエストを受けるHTTPトリガー、クーロンのように一定間隔でAPIを実行するTimerトリガーや、他のAzureサービス(BLOBやCosmos DBやEvent Hub等)へのデータの入力をトリガーとするものなどが存在します。Azure Static Web Appsと連携するAzure FunctionsのAPIでは、HTTPトリガーでなければならないという制約があるため「HTTP trigger」を選択します。

  • APIのトリガーの選択

    APIのトリガーの選択

APIの名称を入力します。ここでは「GetMessage」という名称にしています。

  • APIの名称の入力

    APIの名称の入力

APIの認証レベルを選択します。今回はサンプルアプリのため、だれでもAPIを呼び出せる「Anonymous」を選択します。

  • APIの認証レベルの選択

    APIの認証レベルの選択

ここまで入力を行うと、Azure Functionsのプロジェクトの作成が開始します。しばらく待つと作成が完了します。Azure Functionsのプロジェクト作成後のディレクトリ構成は以下のようになります。

  • Azure Functionsのプロジェクト作成後のディレクトリ

    Azure Functionsのプロジェクト作成後のディレクトリ

Azure Functionsのプロジェクトが作成できたら、APIの実装を修正します。今回は単純に固定のメッセージを返すAPIにします。

サンプルAPI(api/GetMessage/index.js)

module.exports = async function (context, req) {
    context.res = {
        body: { text: "Hello from the Azure Functions API" }
    };
}

APIの定義ファイルも一部修正します。以下の内容になるようにします。

サンプルAPIの定義(api/GetMessage/function.json)

{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
       "get"
      ],
      "route": "message" ・・・①
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}

「route」という項目を定義することで、このAPIを公開した際のパスが決定します(①)。この場合は「/api/message」というパスで公開されます。「/api」は固定で付与され、それ以降のパスが「route」の値によって決定されるようになります。

静的コンテンツの修正とデプロイ

APIの作成が終わったら、静的コンテンツからAPIを呼び出すように修正します。

APIから取得した内容を表示するように修正(index.html)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="styles.css">
    <title>Azure Static Web Apps Sample</title>
  </head>

  <body>
    <main>
      <h1>Hello Azure Static Web Apps!!</h1>
      <p>Loading content from the API: 
        <b id="message">...</b> ・・・①
      </p>
    </main>
  </body>

  <script>
    (async function () {
      let response = await fetch(`api/message`); ・・・②
      let message = await response.json();

      document.querySelector("#message").innerHTML = message.text
    })();
  </script>
</html>

HTMLは、①の箇所のbタグでAPIから取得した内容を表示するように修正しています。APIの呼び出しは、scriptタグ内で行っています。ここで注目するのは、APIの呼び出しが「/api/message」となっている点です(②)。Azure Functionsのホストを明示せずにAPIを呼び出すことができるのは、Azure Static Web AppsとAzure Functionsとの間でシームレスなルーティングが構成されているためです。
なお、scriptタグ内で使用している「async」と「await」というキーワードはJavaScriptの非同期関数と呼ばれる構文のもので、ECMAScript 2017(ES8)で導入されました。以前よりあるPromiseオブジェクトを使った非同期処理をより分かりやすく書くことのできる糖衣構文(シンタックスシュガー)という位置づけです。 asyncキーワードを付与した関数は、暗黙的にPromiseオブジェクトを返却するようになり、関数の呼び出し元で非同期的に処理することができます。asyncを付与した関数内では、awaitキーワードを使用することができます。awaitキーワードは非同期関数に対して付与することができ、付与された関数は非同期的に実行されてPromiseオブジェクトの結果を取得することができます。

ここまで修正ができたら、ファイルをコミットおよびプッシュします。GitHub Actionsでアクションが正常終了していることを確認したら、Webサイトにアクセスします。

  • APIから取得したメッセージを表示したWebサイト

    APIから取得したメッセージを表示したWebサイト

図のようにAPIから取得したメッセージが表示されていれば成功です。

プルリクエストを使ってステージング環境でアプリをテストしよう

ここからは、Azure Static Web Appsの機能を使ってステージング(実稼働前)環境を構築して、本番環境に適用する前のアプリケーションの動作確認を行う方法について説明していきます。

Azure Static Web Appsのステージング環境の仕組み

これまでの説明では、コードを変更したらmasterブランチにコミット、プッシュしてデプロイされたアプリを確認してきました。この時アプリは、いわゆる本番環境、実運用環境と呼ばれる環境へデプロイされていました。実際の開発・運用の現場ではいきなり本番環境へコードをデプロイすることはせず、まずはテスト用の環境へデプロイを行ってUIや機能のテストをした上で本番環境へデプロイするといったワークフローをとることが一般的かと思います。
Azure Static Web Appsでは、このテスト用の環境としてステージング環境を容易に作成することができる機能を提供しています。Azure Static Web Appsが連携しているGitHub Actionsによって、プルリクエストの作成を検知してステージング環境を自動で構築することができます。ステージング環境が用意されることによって、UIや機能の変更のテストを本番環境とは切り離された環境で行うことができるようになります。

なお、Azure Static Web Appsのプレビュー版では、同時に作成できる(運用できる)ステージング環境は最大で1つだけとなっていますので注意して下さい。

新しいブランチの作成とコードの変更

ここからは実際にステージング環境を作っていく手順を説明します。まずはリポジトリに新しいブランチを作成します。Visual Studio Codeの画面左下に表示されているブランチ名を選択して表示されたウィンドウから、「Create new branch」を選択します。

  • Visual Studio Codeで新しいブランチを作成する

    Visual Studio Codeで新しいブランチを作成する

新しいブランチ名を入力します。ここでは「develop」というブランチを新規作成します。

  • developブランチを作成する

    developブランチを作成する

ブランチが作成されると、Visual Studio Codeの左下に表示されているブランチ名も切り替わります。これによってdevelopブランチをチェックアウトした状態であることが分かります。この状態で、コードに変更を加えていきます。「index.html」を選択し、以下のように内容を変更します。

h1タグのメッセージを修正(index.html)

・・・中略

  <body>
    <main>
      <h1>こんにちは、Azure Static Web Apps!!</h1> ・・・①
      <p>Loading content from the API: 
        <b id="message">...</b>
      </p>
    </main>
  </body>

・・・中略

今回は見た目の変化が分かりやすいように、h1タグのメッセージを英語から日本語に変更しました(①)。修正したファイルを保存し、コミットとプッシュを行います。

プルリクエストの作成

変更内容をコミット・プッシュしたら、次にプルリクエストを作成します。プルリクエストはあるブランチの変更内容を別のブランチに取り込む際に他の開発者によってレビューを行い、承認された場合にのみ取り込み(マージ)が実行される、GitHubが提供するチーム開発向けの機能です。

WebブラウザからGitHubにログインし、リポジトリ画面の上部にある「Pull requests」タブを選択するとプルリクエストの作成や現在オープン中のプルリクエストを確認することができます。先程developブランチに対して変更をコミットしたので、developブランチを対象としたプルリクエストを作成するための「Compare & pull request」ボタンが表示されているので、これを選択します。

  • GitHubのプルリクエストタブ

    GitHubのプルリクエストタブ

プルリクエストの作成画面に遷移するので、タイトルと変更内容について任意で記入していきます。記入後、「Create pull request」ボタンを選択してプルリクエストを作成します。

  • プルリクエストの新規作成

    プルリクエストの新規作成

プルリクエストを作成すると、プルリクエストの詳細画面に遷移します。

ステージング環境での動作確認

プルリクエストの詳細画面では、レビュアーからの指摘内容や追加のコミット情報などを時系列で確認することができます。今回のようにAzure Static Web Appsと連携しているリポジトリの場合は、GitHub Actionsがリポジトリに組み込まれているためプルリクエストの作成や更新に反応して自動的にGitHub Actionsが実行されます。GitHub Actionsの実行が完了すると、以下の画面のように「github-actions」という名前のボットからメッセージが届きます。

  • プルリクエストの詳細画面

    プルリクエストの詳細画面

ボットのメッセージ内に「Your stage site is ready!」と記載されていれば、ステージング環境へのデプロイが成功しています。同じメッセージ内に記載されているURLを選択すると、デプロイされたステージング環境のアプリにアクセスすることができます。

  • ステージング環境のアプリの画面

    ステージング環境のアプリの画面

表示されている内容が、先程developブランチの「index.html」に対して行った変更通りとなっていることが分かるかと思います。また、サイトのURLも本番環境のものとは異なるURLとなっており、完全に別のサイトとしてデプロイされていることも分かります。
現在デプロイされている環境については、Azureポータルからも確認することができます。Azureポータルで該当のAzure Static Web Appsのリソースを選択し、メニューから「環境」を選択することで、現在の環境を一覧で確認することができます。

  • デプロイされている環境一覧

    デプロイされている環境一覧

「運用」、「ステージング」のカテゴリーにそれぞれ本番環境とステージング環境のデプロイ内容が記載されています。どのブランチが対象となっているかや、現在のサイトの状態などを確認することができます。それぞれの環境ごとにある、「参照」リンクからサイトを表示することもできます。

プルリクエストのマージ

最後に作成したプルリクエストをマージして、ステージング環境の内容が本番環境に反映されることを確認しましょう。GitHubに戻り、プルリクエストを表示します。プルリクエストの詳細画面の下部にある「Merge pull request」ボタンを選択し、続いて表示される確認用の「Confirm merge」ボタンを選択すると、プルリクエストがdevelopブランチからmasterブランチにマージされます。

  • プルリクエストのマージ

    プルリクエストのマージ

プルリクエストをマージすると、GitHub Actionsによって本番環境へのデプロイが実行されます。デプロイ完了後、本番環境のサイトにアクセスすると先程までステージング環境に表示されていたものと同じ内容が表示されていることが分かるかと思います。 なお、プルリクストをマージするとステージング環境は削除されてしまいます。ステージング環境用のサイトへアクセスすると404エラーとなり、Azureポータル上でも環境の一覧からステージング環境が削除されていることが分かります。

  • プルリクエストのマージ後の環境一覧

    プルリクエストのマージ後の環境一覧

これは、プルリクエストをマージしたタイミングでGitHub Actionsがステージング環境の削除も行っているためで、これによって手動でのステージング環境の削除忘れや手間を省くことができるようになっています。

まとめ

全2回に分けてAzure Static Web Appsについて紹介しました。単なる静的サイトのホスティングに留まらず、Azure FunctionsによるAPIの利用が可能であったりGitHubを活用したCI/CDのサポートといった開発から運用までのライフサイクルを包括したサービスであることが分かったかと思います。
次回は地図や位置情報などを取り扱うAzure Mapsについて紹介する予定です。

WINGSプロジェクト 秋葉龍一著/山田祥寛監修
<WINGSプロジェクトについて>テクニカル執筆プロジェクト(代表山田祥寛)。海外記事の翻訳から、主にWeb開発分野の書籍・雑誌/Web記事の執筆、講演等を幅広く手がける。一緒に執筆をできる有志を募集中