Azure Static Web Appsを試してみた３【API（Azure Functions）連携】

# はじめに

以前の記事[Azure Static Web Appsを試してみた２【Azure DevOpsと連携】](https://qiita.com/akihiro-nakano/items/d072542312a2c51a4743)でAzure Static Web Appsと、DevOpsとの連携について書きました。
今回は、いよいよAPI（Azure Functions）との連携を試してみようと思います。

## 前提
以下の拡張機能をVisual Studio Codeに事前インストールしておく必要があります。

 - [Azure Functions拡張機能](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions)

## フォルダ構成
Static Web Apps（Free）の制約としてAngularプロジェクトとFunctionsのプロジェクトは同じリポジトリーである必要がります。
そのため、Angular(FrontEnd)とFunctions（BackEnd)はそれぞれ、フォルダ分割しておかないと後々大変になるので、はじめから意識しておくとよいです。

## Angular プロジェクトの作成
以下のコマンドを実行し、Angularのひな形プロジェクトを作成します。
(本コマンドはgitからクローンしたルートフォルダで実行してください。)

```cmd
> ng new angular-swa
```

### API呼出の実装
まずは、HTTPClientが利用できるように、app.module.tsを修正します。

``` typescript
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { HttpClientModule } from '@angular/common/http';　// <- 追加

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule　// <- 追加
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

```

続いて、ボタンクリックでFunctionsを呼べるように実装します。

- app.component.html

``` html
<button type="button" (click)="callAPI()"> API実行</button>
<p>結果：{{result}}</p>
```

- app.component.ts

``` typescript
  public result = '';

  constructor(private httpClient: HttpClient) { // コンストラクタでhttpClientをインジェクション
  }

  callAPI() {
    // Azure Functionsを呼出
    this.httpClient.get('/api/GetMessage').subscribe((data: any) => {
      this.result = data.text;
    });
  }
```

## Functionsプロジェクトの作成
前提で紹介したVisual Studio CodeのAzure Functions拡張機能でFunctionsのひな形プロジェクトを作成します。
まずは、Azure Functionsのひな形プロジェクトを作成するフォルダを作成します。
（今回はapiとします）
左側メニューのAzureを選択し、Functionsタブで「Create New Project」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226944/b1b5bac6-616d-ff5c-3763-2252f3026e54.png)

- Browserで作成したAPIフォルダを選択
- 言語でJavascriptを選択
- トリガーはHTTP Triggerを選択
- 関数の名前は任意を入力（今回はGetMessage)
- 認証はAnonymousを選択(認証無し）

## Proxy定義
Static web appsでは、AngularプロジェクトとFunctionsプロジェクトは同じサーバーで動作します。
そのため、Angular プロジェクトは同一サーバーにリクエストするように実装します。
これだと、ローカル環境では都合がわるいので、[AngularのProxy機能](https://angular.jp/guide/build#%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%B8%E3%81%AE%E3%83%97%E3%83%AD%E3%82%AD%E3%82%B7)を利用します。

- proxy.conf.jsonに以下を設定し、srcフォルダ直下に配置

``` json
{
  "/api": {
    "target": "http://localhost:7071",
    "secure": false
  }
}
```
- angular.jsonのserveにproxyConfigを追加

``` json
"architect": {
  "serve": {
    "builder": "@angular-devkit/build-angular:dev-server",
    "options": {
      "browserTarget": "your-application-name:build",
      "proxyConfig": "src/proxy.conf.json"
    },
```

## ローカルでの動作確認
作成したそれぞのフォルダで以下のコマンドを実行し、動作を確認します。

- Angular

```
> ng serve
```

- Functions

```
> func start
```

ブラウザから「http://localhost:4200 」へアクセスし、ボタンクリックで
無事、Proxy経由でFunctionsからのレスポンスを受け取ることが出来ました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226944/095a9ffc-8191-2543-5023-f31be8a1bbce.png)

## DevOps PipelineのYAML定義の修正
前回のYAMLに以下の修正を加えます。

- npm install 前にリポジトリのAngularプロジェクトのルートフォルダへ移動***(これをしないとコマンドNot Foundとなります)***
- 「app_location」にAngularプロジェクトのルートフォルダを指定(angular-swa)
- 「api_location」にFunctionsプロジェクトのルートフォルダを指定(api)
- 「output_location」にAngularプロジェクトのBuild後の出力先フォルダを指定(dist/angular-swa)


``` yaml
# Node.js with Angular
# Build a Node.js project that uses Angular.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript

trigger:
- main

pool:
  vmImage: ubuntu-latest

steps:
- task: NodeTool@0
  inputs:
    versionSpec: '12.x'
  displayName: 'Install Node.js'

- script: |
    npm install -g @angular/cli@9.1.15
    cd ./angular-swa
    npm install
    ng build --prod
  displayName: 'npm install and build'

- task: AzureStaticWebApp@0
  inputs:
    app_location: "angular-swa"
    api_location: "api"
    output_location: "dist/angular-swa"
  env:
    azure_static_web_apps_api_token: $(deployment_token)

```

上記を含めてリポジトリーにコミットすることで、自動でStatic Web AppsへAngular/Functionsがデプロイされ、動作が確認できます。

## まとめ
今回で、Azure Static Web Apps/Azure DevOps上でのAngular/Functionsを利用した静的Webアプリ（SPA）までが出来ました。
次回以降は、認証([こちら](https://qiita.com/akihiro-nakano/items/b67e7e49d35d336f7f69))か外部のFunctionsとの連携を検討していこうと思います。