Azure Functions入門: サーバーレスで簡単API構築ガイド

最近APIを立てようと思ったら、Azure Functionで構築することが殆どになってきました。
個人で開発する時もサクッと作れるので、開発体験もいいし、API認証とログも勝手に用意されているので、嬉しいです :smile: 
そんなAzure Functionsのいつもの構築手順を残しておこうと思います。

公式ドキュメントはこちらですので、本記事は簡易版としてみてもらえれば幸いです :+1:

https://learn.microsoft.com/ja-jp/azure/azure-functions/create-first-function-cli-typescript

## 先にAzure Functions の箱だけ作る

`az functionapp`を覚えた方がいいことは分かりつつ、慣れているWebコンソール上から作ります。

https://portal.azure.com/#create/Microsoft.FunctionApp

開いたらプランを選びます。今回は個人用なので「Consumption（消費）」プランを使おうと思います。

![スクリーンショット 2024-12-23 22.16.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531257/b18fc71a-ff69-daef-9019-096c0b721302.png)

なおConsumptionプランはめちゃ安いです。詳細は以下！

https://azure.microsoft.com/ja-jp/pricing/details/functions/

そしたら順に以下を埋めていきます。
![スクリーンショット 2024-12-23 22.21.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531257/6c521f64-b93d-d7f8-7179-16648715cc97.png)

確認および作成から作成しちゃいます。これでOK！
（本当はストレージアカウントの名前整えたりするけど今回は省略）

あとはコードを用意してデプロイするだけで大丈夫です。

## CLIを準備

Azure Functions用のCLIツールを用意が必要です。
```shell
brew tap azure/functions
brew install azure-functions-core-tools@4

# 動作確認
which func
```
これをつかって、コードの生成・デバック・デプロイ、をしていきます。

## コード生成

事前にプロジェクト用ディレクトリを用意してから
```shell
mkdir project && cd project
```

`new` コマンドでコード生成できます。
```shell
func new --name EasyToCreate --template "HTTP trigger" --authlevel "function" --worker-runtime node --language typescript
```
`init`コマンドもありますが、実行ファイルが生成されないので、`new`の方が楽でおすすめです。

以下実行ログ
``` 
The new Node.js programming model is generally available. Learn more at https://aka.ms/AzFuncNodeV4
Writing package.json
Writing .funcignore
Writing tsconfig.json
Writing .gitignore
Writing host.json
Writing local.settings.json
Writing /tmp/project/.vscode/extensions.json
Running 'npm install'...Select a number for template:HTTP trigger
Function name: [httpTrigger] Creating a new file /Users/uetash/tmp/project/src/functions/EasyToCreate.ts
The function "EasyToCreate" was created successfully from the "HTTP trigger" template.
```
生成されるのは以下
```
.funcignore
.gitignore
.vscode/
host.json
local.settings.json
node_modules/
package-lock.json
package.json
src/
tsconfig.json
```

その後、gitの初期化もついでに行います。
ちゃんと`.gitignore`も生成されているので、`init`して、`commit`するだけでOKです。
```
git init
git add .
git commit -m "chore: init"
```

## 起動

APIを起動してみます。
```
npm run start

...

Functions:
	EasyToCreate: [GET,POST] http://localhost:7071/api/EasyToCreate
```
起動したので動作確認します。
```
curl http://localhost:7071/api/EasyToCreate

Hello, world!
```

いいですね。

## コードをいじる

生成されたコードはテンプレートから作られています。
ただ、大抵の場合APIはJSONを返したいので、以下のようにコードを書き換えます。

```typescript
import {
  app,
  HttpRequest,
  HttpResponseInit,
  InvocationContext,
} from "@azure/functions";

// この関数は、HTTPリクエストを受け取り、レスポンスを返します。
// 関数名`EasyToCreate`は自由に変更可能です。
export async function EasyToCreate(
  request: HttpRequest, // HTTPリクエストの内容を表すオブジェクト
  context: InvocationContext // 実行時のコンテキスト（ログの記録などに使用）
): Promise<HttpResponseInit> { // HTTPレスポンスを非同期で返すPromise
  // リクエストのボディをJSONとして解析し、オブジェクトとして取得します。
  const body = (await request.json()) as {
    name?: string;
  };

  // ボディに`name`が指定されていない場合、デフォルト値として`"world"`を使用します。
  const name = body.name || "world";

  // ログに「Hello」メッセージを記録します。`context.info`は情報ログの出力に使用します。
  context.info(`Saying hello to ${name}...`);

  // HTTPレスポンスを構成して返します。
  // ここではJSON形式のレスポンスを返すため、`jsonBody`プロパティを指定します。
  return {
    jsonBody: {
      message: `Hello, ${name}!`, // レスポンスメッセージ。リクエストの名前を含む挨拶文
    },
  };
}

// Azure FunctionsアプリケーションにHTTPエンドポイントを登録します。
// 第一引数はエンドポイント名です（デプロイ後に識別するための名前）。
// 第二引数はエンドポイントの設定オブジェクトです。
app.http("Hello World", {
  methods: ["POST"], // このエンドポイントはHTTP POSTメソッドに対応します。
  authLevel: "function", // 認証レベルを指定します。`function`は関数キーを必要とします。
  handler: EasyToCreate, // リクエストを処理する関数を指定します。
});
```

これでJSONのAPIができました。簡単ですね。
余談ですが、開発中はホットリロードが欲しいので
```
npm run watch
```
を裏で実行しつつ
```
npm run start
```
を実行してローカルでの動作を確認してます。
動作確認すると以下のように表示されました。
```
curl -sX POST http://localhost:7071/api/HelloTrigger \
  -d '{"name": "uetash"}' | jq .

{
  "message": "Hello, uetash!"
}
```
:+1:


## デプロイする

このコマンドを叩くだけです！　
```shell
# ビルド
npm run build

# func azure functionapp publish <application名>
func azure functionapp publish easy-to-create
```

これで終わりです :smile: 動作確認をすると
```shell
curl -sX https://easy-to-create.azurewebsites.net/api/HelloTrigger \
  -H "x-functions-key: ${API_KEY}" # Web上から関数キー取得＆指定
  -d '{"name": "uetash"}' | jq .

{
  "message": "Hello, uetash!"
}
```
できた！

## まとめ

ここまで、Azure Functions を Azure ポータル上で作成し、CLI (func) や TypeScript を用いてサクッと API を構築する流れを紹介しました。
ローカルでの開発／デバッグも簡単で、デプロイはワンコマンドで完了できる手軽さが大きな魅力です。

以下に、改めて感じているメリットをいくつかまとめてみます。（生成AIでまとめました）

```
サーバーレスでインフラ管理が不要
* サーバーの設定や運用の手間を完全に省けます。
* 開発者はアプリケーションのロジックに集中できるので、プロジェクトの進行が加速します。

コスト効率が抜群
* 従量課金モデル: 実行した分だけ課金されるため、初期費用や無駄なコストを抑えられます。
* 自動スケール対応: 負荷が増えれば自動的にスケールアウト、閑散時にはリソース消費を最小限に。

開発・運用がスムーズ
* 簡単デプロイ: CLIツールでワンコマンドデプロイ可能。
* ローカル開発: ホットリロードやデバッグが標準装備で、素早く試行錯誤ができます。
* 拡張性: FunctionsとAzureの他サービス（CosmosDB、Storageなど）の統合がスムーズ。

認証・監視が簡単
* 認証の標準サポート: APIキーやAzure AD認証がすぐに利用可能。
* ログの自動収集: Application Insightsとの統合でエラーやリクエストを一元管理。

小規模から大規模まで柔軟に対応
* 個人の試作やサイドプロジェクトから、トラフィックが集中する大規模システムまで幅広く対応。
* 必要に応じたスケールが可能で、無駄がありません。
```

私の愛してやまないAzure Functionsが、より多くの人に使われてもっといい製品に成長してもらえるように祈ります :pray:


## 余談：CosmosDBとの組み合わせについて

Azure Functions は完全にステートレスな実行基盤なので、データを保存したい場合はどこかに格納する必要があります。
その際 Cosmos DB は、Azure が提供するフルマネージドな NoSQL データベースで、Functions との相性が非常に良いです :+1:

頻繁にアップデートもあり、ベクトル検索はもちろん、最近では全文検索もできるようになりました！コストも安いので、AzureでDBに困ったら、最初に挙げられる選択肢かと思います。CosmosDBもよく使うので、またいつかCosmosDBの記事も書いてみようと思います。