VSCodeでのAzure Static Web Appsのプロジェクト作成手順とローカル開発手順

Azure Static Web Apps って便利ですよね。
JAMStack や SPA のアプリケーションを Azure にホストするときに適したサービスです。

https://azure.microsoft.com/ja-jp/services/app-service/static/

Azure Static Web Apps の特徴の一つとして、API を構成する Azure Functions を Azure Static Web Apps に内包することができるということです。
API を内包しているため、HTML/JavaScript から API を呼び出す際は localhost に対して通信をすることになります。

簡単に図で表すとこのようになります。
![azure-static-web-apps.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1136/542a9815-ba97-4c56-432a-775deffc04c7.png)

では、この構成をローカル環境で開発するとして、はじめにどのようにプロジェクトを作成すると良いでしょうか。
公式ドキュメントでも解説されていますが、少し分かりにくかったので自分なりに手順をまとめてみます。

https://docs.microsoft.com/ja-jp/azure/static-web-apps/local-development?WT.mc_id=AZ-MVP-5002209

https://docs.microsoft.com/ja-jp/azure/static-web-apps/add-api?WT.mc_id=AZ-MVP-5002209

# 前提
これから説明する手順では以下の環境を前提とします。
説明の範囲においては、プログラミング言語での違いはありません。
他のツールを使っている場合は適宜読み替えてください。  

- [WSL2](https://docs.microsoft.com/ja-jp/windows/wsl/)
- [Ubuntu 20.04](https://www.microsoft.com/ja-jp/p/ubuntu-2004-lts/9n6svws3rx71)
- [Visual Studio Code](https://azure.microsoft.com/ja-jp/products/visual-studio-code/)
  - [拡張機能: Remote Development](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack)
  - [拡張機能: Azure Functions](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions)
  - [拡張機能: Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)
- [Azure Functions Core Tools](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-run-local?tabs=linux%2Ccsharp%2Cbash&&WT.mc_id=AZ-MVP-5002209)

# プロジェクトの作成
まずはプロジェクト用のフォルダを作成します。
Webアプリケーション用の `app` フォルダと、API用の `api` フォルダも作成します。

```sh
mkdir static-web-app-sample
cd static-web-app-sample/
mkdir app
mkdir api
```

# Visual Studio Codeでプロジェクトを開く
`code .` コマンドを実行し、プロジェクトを Visual Studio Code で開きます。

# Azure Functions のプロジェクトを作成する
まずは `api` フォルダに Azure Functions のプロジェクトを作成します。
`func` コマンドで作成してもいいですが、今回は Visual Studio Code を使って作成します。

コマンドパレットを開き、`Azure Functions: Create New Project...` を選択します。
![2021-08-12_11h12_33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1136/b3537771-978d-b84a-5b80-eb26f42a787e.png)

Azure Functions のプロジェクトの保存先に `api` フォルダを指定することを忘れないようにしましょう。 
また、承認レベルは Anonymous を選択します。
![2021-08-12_11h13_42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1136/1dc6b900-5c03-24ac-e59f-464bce5d0b80.png)
![2021-08-12_11h13_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1136/ed920f6d-4878-335a-dc17-34ddbe1562fd.png)

```js:api/HttpTrigger1/index.js
module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    const name = (req.query.name || (req.body && req.body.name));
    const responseMessage = name
        ? "Hello, " + name + ". This HTTP triggered function executed successfully."
        : "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.";

    context.res = {
        // status: 200, /* Defaults to 200 */
        body: responseMessage
    };
}
```

## Azure Functions を実行してみる
ひとまず API は作成することができたので、きちんとデバッグ実行できることを確認しておきましょう。（メニュー ＞ 実行 ＞ デバッグの開始）

Visual Studio Code の Azure Functions 拡張機能では、開いているプロジェクトに含まれる関数を表示してくれます。
関数を右クリックし「Execute Function Now」から、関数を実行することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1136/53dc024c-3617-949e-a4b5-8a1cd608884d.png)

# Web アプリケーションを作成する
次に Web アプリケーションを作成します。
今回はあくまでサンプルなので、JSフレームワークは特に使いません。

`app` フォルダに以下の内容の `index.html` を作成します。

```html:app/index.html
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Azure Static Web Apps Sample</title>
</head>

<body>
  <div id="message"></div>
  <script>
    fetch('/api/HttpTrigger1?name=tsubakimoto')
      .then(response => response.text())
      .then(text => document.getElementById('message').textContent = text);
  </script>
</body>

</html>
```

`fetch('/api/HttpTrigger1?name=...')` をみていただくとわかるように、同じドメインにある API をコールしています。

## Web アプリケーションを動かしてみる
API を実行した状態で、コマンドパレットから `Live Server: Open with Live Server` を実行します。
[Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) とは、開発環境向けの簡易的な Web サーバーを利用することができる拡張機能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1136/67fa8a9e-3798-d5ed-cf00-9057f1480702.png)

`http://localhost:5500/app/` にアクセスするとページ自体は表示できていますが、API の結果を正しく取得することができていないようです。
`localhost:5500` から `localhost:7071` にある API にアクセスできていないことがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1136/08d8513b-c8e7-af9b-4d9c-fa1ddde5b9e9.png)

# Visual Studio Code のプロキシ設定をする
`http://localhost:7071/api/` で実行中の API にアクセスできるようにするために、[Live Server のプロキシ設定](https://github.com/ritwickdey/vscode-live-server/blob/master/docs/settings.md)を行います。
`.vscode/settings.json` を開き、以下を追加します。

```json
"liveServer.settings.proxy": {
   "enable": true,
   "baseUri": "/api",
   "proxyUri": "http://127.0.0.1:7071/api"
}
```

Node.js の Azure Functions であれば、以下の構成となります。

```json:.vscode/settings.json
{
    "azureFunctions.deploySubpath": "api",
    "azureFunctions.postDeployTask": "npm install (functions)",
    "azureFunctions.projectLanguage": "JavaScript",
    "azureFunctions.projectRuntime": "~3",
    "debug.internalConsoleOptions": "neverOpen",
    "azureFunctions.preDeployTask": "npm prune (functions)",
    "liveServer.settings.proxy": {
        "enable": true,
        "baseUri": "/api",
        "proxyUri": "http://127.0.0.1:7071/api"
     }
}
```

再度 Live Server を起動し直して `http://localhost:5500/app/` にアクセスすると、きちんと API がコールできたことが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1136/d4093f8f-209a-5dd1-a254-96d0d4e66fbb.png)

# Azure Static Web Apps にデプロイする
ローカル環境でアプリを動かすことができたので、Azure Static Web Apps にデプロイしてみましょう。  
基本的な手順は公式ドキュメントどおりです。

https://docs.microsoft.com/ja-jp/azure/static-web-apps/get-started-portal?tabs=vanilla-javascript

今回の手順では以下のようにビルド設定しました。

- ビルドのプリセット：Custom
- アプリの場所： /app
- APIの場所： /api
- 出力先：未指定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1136/d21ac905-fc3a-7215-ed53-f7d6e77cef8e.png)

GitHub Actions でのデプロイの完了を待って、Webページにアクセスします。
きちんと `/api/HttpTrigger1` つまり、Azure Static Web Apps に内包された Azure Functions にアクセスできていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1136/6f0fd19b-b46d-0cd5-e0fa-9f4aaff38ba2.png)

---

ローカル開発環境を整えて、Azure Static Web Apps を上手に使っていきましょう！
今回のサンプルコードは GitHub で公開していますので参考にしてみてください。

https://github.com/tsubakimoto/static-web-app-sample