[Azure] Static Web Apps + Functions + MySQLでWebアプリをつくる

# はじめに

**※本記事は続き物の最終回的な位置づけとなっており、単体で Static Web Apps + Functions + MySQL のすべてに言及しているものではありません**

[前回までの記事](#前回までの記事)で、Azure Functions + MySQLを使用してバックエンド部分を作成しました。  
今回はそこにAzure Static Web Appsを使用したフロント部分を追加して、シンプルなWebアプリケーションとして動かすことを目指します。  
フロント部分はReactを使うことにしました。  
※"最低限動くもの"を目指すので、認証などのセキュリティ関連の実装は含みません

ついでに、Azure FunctionsアプリをGithubと連携して自動デプロイさせるようにします。

## 構成イメージ

赤くなっている部分が今回の範囲。

![AzureCLI.drawio (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884047/535475ca-86b9-41fc-1950-540876292025.png)

> Azure Static Web Appsでは、`マネージドなFunctionsアプリ`と`独自の(自前の)Functionsアプリ`の2通りの構成にできますが、既に作成済みなのと、前者はJavaに対応していないという理由で、今回は後者の構成で進めます。  
> →[Azure Functions による Azure Static Web Apps での API のサポート](https://learn.microsoft.com/ja-jp/azure/static-web-apps/apis-functions)

## 前回までの記事

https://qiita.com/fsdg-nagumo/items/5eec1e9b66d33e3f4687

https://qiita.com/fsdg-nagumo/items/4c069fe09676ac1b3326

# 動作環境

作業時の環境は以下の通りです。

- OS
  - Windows 11 Pro (21H2)
- React 18.2.0

# フロント側のアプリを作成

1. Reactを使用してフロント側のアプリを普通に作る。  
   ※作成方法はここでは扱いません  
2. 作成したアプリをGithubにpushする。  
   [ソースはこちら](https://github.com/azure-learn-796/azure-static-web-app) 

## 実装時のポイント

`APIのエンドポイント`、`APIキー`は環境（開発環境・運用環境等）によって変化する可能性があるので、環境変数に持たせます。  
※[環境変数の設定方法は後述](#環境変数の設定)

プログラム内で環境変数は以下のように取得できます。

```typescript
// Reactの場合、変数名が"REACT_APP_"で始まる必要がある
const VARIABLE: string = process.env.REACT_APP_VARIABLE;
```

# フロント側のアプリをAzureにデプロイ

Azure Portalで「静的Webアプリ」で検索し、`作成`  
内容入力して、`確認および作成`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884047/872d24e5-e725-17cb-87f2-298c9518751b.png)

今回はGithubと連携させるので `デプロイの詳細` > `ソース` で `Github` を選択。  
ログインを求められたらログインする。  

|設定名|設定内容|
|---|---|
|組織|組織を選択（個人アカウントの場合はアカウント名と同じ）|
|リポジトリ|デプロイ対象のリポジトリを選択|
|分岐|デプロイ対象のブランチを選択|
|ビルドのプリセット|フレームワークを選択。今回はReact|
|アプリの場所|リポジトリルートから見たアプリのルードディレクトリを指定。|
|APIの場所|マネージドFunctionsを使用する場合は指定。今回は使用しないので空|
|出力先|ビルドされたファイルの出力先|

作成が完了すると、GithubのリポジトリにWorkflowの設定ファイルが追加され、ビルド＆デプロイが開始されます。  
この設定ファイルを編集することで、ビルド設定などを調整できます。  
ここでは特に変更はありません。

```yaml:.github/workflows/azure-static-web-apps-xxxxxxxxxxx.yml (xxxxxxxxxxxの部分は毎回違う)
name: Azure Static Web Apps CI/CD

on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize, reopened, closed]
    branches:
      - main

jobs:
  build_and_deploy_job:
    if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
    runs-on: ubuntu-latest
    name: Build and Deploy Job
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true
      - 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_XXXXXXXX }}
          repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
          action: 'upload'
          ###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
          # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
          app_location: '/' # App source code path
          api_location: '' # Api source code path - optional
          output_location: 'build' # Built app content directory - optional
          ###### End of Repository/Build Configurations ######

  close_pull_request_job:
    if: github.event_name == 'pull_request' && github.event.action == 'closed'
    runs-on: ubuntu-latest
    name: Close Pull Request Job
    steps:
      - name: Close Pull Request
        id: closepullrequest
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_XXXXXXXX }}
          action: 'close'
```

# 環境変数の設定

## ローカル実行時

ローカル環境で実行する場合は `.env.local` に環境変数を設定します。

```properties:.env.local
REACT_APP_API_ORIGIN=<APIのオリジン>
REACT_APP_API_KEY=<Azure Functionsアプリのアプリキー>
```

## Azureデプロイ時

ここで注意したいのが、**環境変数はビルド時に解決**されるという点です。  
ビルドプロセスが動くのは**Azure側ではなくGithub側**なので、Github側にSecretsとして環境変数を登録し、Workflow設定ファイルから参照するようにします。  

最初この流れが理解できていなくて、Functionsの時と同様にAzure側のアプリケーション設定で何とかしようと悪戦苦闘してました・・・  
(フロントエンドやってる人には鼻で笑われるでしょう・・・)

Static Web Appsの「アプリケーション設定」は、マネージドFunctionsのための設定なんですね。

### GithubのSecrets登録

Githubにログインして `<リポジトリ>` > `Settings` > `Secrets` > `Actions` > `New repository secret` から登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884047/f1819e2d-9a33-df62-2872-cf5f1ffb049a.png)

|設定名|設定値|
|---|---|
|API_ORIGIN|Azure Functionsアプリのホスト|
|API_KEY|Azure Functionsアプリのアプリキー|

※設定名はWorkflow設定ファイルの方と合っていれば何でも良い

### Workflow設定ファイルの修正

登録したSecretsを参照して環境変数を設定するよう修正します。  
pushすると自動的にビルド＆デプロイが実行されます。楽。

```yaml:.github/workflows/azure-static-web-apps-xxxxxxxxxxx.yml
name: Azure Static Web Apps CI/CD
# ・・・
jobs:
  build_and_deploy_job:
#   ・・・
    steps:
      - name: Build And Deploy
    #   ・・・
    #   ↓追加↓
        env: # ビルド時に参照する環境変数
          REACT_APP_API_ORIGIN: ${{ secrets.API_ORIGIN }} # APIのホスト
          REACT_APP_API_KEY: ${{ secrets.API_KEY }}       # APIキー
    #   ↑追加↑

#   ・・・
```

# FunctionsのCORS設定

:::note warn
**2023/02/23 追記**
記事執筆時点では筆者の知識不足のため、面倒な方法になっていますが、Static Web Apps と 独自関数は簡単にリンクさせることができ、CORSなどの設定も不要です。
更にこの方法だとAPI側の認証もStatic Web Apps の設定で一括でできたりと何かと便利なので、オススメです！
https://learn.microsoft.com/ja-jp/azure/static-web-apps/functions-bring-your-own
:::

ここまででフロント側としては構築完了なのですが、このままではAPIにアクセスすることができません。  
というのも、フロント側のオリジンとAPI側のオリジンが一致しておらず、[CORSエラー](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS/Errors)となってしまうためです。

そこで、Azure Functions側でCORSの設定を行う必要があります。

Azure Portalで `関数アプリ` > `<関数アプリ名>` > `CORS` で、フロント側のURLを追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884047/ac2ee343-12a1-dca9-dec7-2ff57577340a.png)

フロント側のURLは、`静的Webアプリ` > `<アプリ名>` > `概要` > `URL` で確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884047/ac654dc2-14be-155a-54e1-ab3e6d6d746d.png)

これで画面からAPIにアクセスできるようになりました。  
(APIへのリクエストが正常に返ってきているのが確認できます)

![Create-React-App-Sample-Azure-Static-Web-Apps-Demo-2022-10-29-17-55-44.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884047/e27686cd-80b8-b244-6ad5-8c7ae1c43467.gif)

# Functionsの自動デプロイ設定

最後に、現状Functionsアプリは手動でデプロイする必要があるため、ブランチへのpushをトリガーに自動でデプロイされるようにします。

Azure Portalで `関数アプリ` > `<関数アプリ名>` > `デプロイセンター` から設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884047/8a73b5da-bd71-61f3-39f6-a60406b7ec23.png)

`ソース` で `Github` を選択。  
もしログインを求められたらログインする。  
`保存` ボタンで確定する。

|設定名|設定内容|
|---|---|
|組織|組織を選択（個人アカウントの場合はアカウント名と同じ）|
|リポジトリ|デプロイ対象のリポジトリを選択|
|ブランチ|デプロイ対象のブランチを選択|

保存すると、Static Web Appsと同様、GithubのリポジトリにWorkflowの設定ファイルが追加され、ビルド＆デプロイが開始されます。

以上  

# おわりに

フロントエンドからバックエンドまでの一通りを、面倒なミドルウェアの設定も不要で、サクッと（？）構築できました。  
環境面をあまり意識せずコードに集中できるのは、やはりありがたいですね。   

最後までお付き合いくださり、ありがとうございました！

# 参考文献

- [Azure Functions による Azure Static Web Apps での API のサポート](https://learn.microsoft.com/ja-jp/azure/static-web-apps/apis-functions) 
- [CORSのエラー](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS/Errors)