Postman Flowsを使って外部公開のAPIを作成する

# はじめに

[Postman Flows](https://www.postman.com/product/flows/)はGUIを使ってノーコードでAPIの呼び出しフローを作成するサービスです。
Momento会 #8にて Momento Topicに入ってきたデータをTiDBに保存する際の保存APIの構築にFlowsを使ってみたので、利用時のTipsなどを書いて見たいと思います。

https://speakerdeck.com/bohnen/postman

## 実装内容
* Momento Topicsで配信されたJSONをTiDBに保存する。
    * Momentoからはwebhookを使い配信時にAPIをコールする
    * TiDBへの保存は、TiDB側で用意したAPIが存在するのでそれをコール

要はMomento Topicsのイベントで起動される Lambda + API Gatewayのようなものですが、単に来たJSONを左から右に流すだけですし、入り口も出口もREST APIなのでPostmanを使うと一元的に管理できたりテストできてよさそうです。

## 準備 - 送信元APIをPostmanのcollectionとして取り込む

### 送信先のAPI定義の取り込み

送信先のAPIをPostman側にImportしておくと、Postmanの変数定義が利用できて加工が柔軟になります（後述）。ここでは、TiDBのAPI定義を読み込みます。（TiDB側でのAPI作成についてはここでは割愛します）

このような定義になっていますが、この定義をOpenAPI形式のJSONファイルで取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/0306e9f9-891d-5819-927a-6fb0f85602b2.png)

これをImportすることで、自分のワークスペースにCollectionsとして取り込むことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/3239e138-90a1-4c66-c0e6-9ecc16792967.png)

### 送信先APIの各種パラメーターを変数化する

APIコール時に設定するrequest parameterやbody、認証情報などを変数にしておきます。
今回の呼び出し先のAPIはrequest parameterはなし、bodyのみ。認証情報はダイジェスト認証となっています。

まず認証情報を設定します。認証情報はCollectionsのトップレベルで設定します。ここでは `{{PUBLIC_KEY}}` `{{PRIVATE_KEY}}`で設定します。 Postmanでは変数は `{{変数名}}` で設定します。この変数はFlowsの中はもちろん、様々な方法で設定することができます。詳細は[Postman川崎さんの徹底解説](https://qiita.com/yokawasa/items/05913df60aea07395903) をご覧ください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/a4e601ba-ad2b-0e47-5f1f-bd0e6f9092b0.png)

Bodyについても変数を使って定義します。今回は送信元とJSONの形は一緒なので、送信元から来たJSONをそのまま設定することにして、全部変数にしています。個別APIのbodyタブで、`raw`を選択して `{{rate_json}}`と入力しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/aa8f7744-fa4d-322a-608d-0cb5ac025009.png)

ここまでできたら、SAVEしておきます。

### 環境変数を設定する

さて先ほど定義した3つの変数 `{{PUBLIC_KEY}}` `{{PRIVATE_KEY}}` `{{rate_json}}`を環境変数で設定します。環境はAPIテスト用の`TEST`と本番用の`PROD`を作成します。`TEST`では`{{rate_json}}`にJSON文字列をセットしますが、PRODではこれは呼び出し元のbodyをセットしますので不要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/a194c33f-14bf-56c8-847b-aef60254db1e.png)

:::note
作成した後SAVEするのを忘れずに
:::

環境変数ができたら、環境を指定してAPIのテストを実行してみます。API画面の右上に環境選択ドロップダウンがあります。ここで環境を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/9cd619a1-2889-8a6a-05ec-8b9a449a3229.png)

あとは実行するだけです。正しくheaderやbodyが設定できているか、consoleでチェックしましょう。
環境変数が正しく設定されているかは、環境選択ドロップダウンの隣の`Environment Quick Look` で確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/2db19645-a17e-0f30-3d64-20d3b24ec824.png) 
※@nagix さんから教えていただきました。


## Flowsの作成

### API呼び出しの設定

Flowsで新規作成を行って、`Start`の直後でAPI呼び出しを追加します。API呼び出しのドロップダウンからCollectionsのAPIが選択できるので、先に作成した`rate_jpn`を指定します。
自動的にbodyの変数定義を認識して、変数として表示してくれます。このとき、環境が選べるのでPRODを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/a0926a44-1fea-7687-6288-9c2da89fee32.png)

### jsonの変換

呼び出し元のMomentoが渡すbodyのjson定義は下記のようになっているので、ここから`text`のjsonを抜き出して`rate_json`に設定すればよいのですが・・・

```json
{
  "cache": "momento-exchange",
  "topic": "rate",
  "event_timestamp": 1703245656262,
  "publish_timestamp": 1703245656262,
  "topic_sequence_number": 418,
  "token_id": "",
  "text": "{\"best_bid\":6594882,\"best_ask\":6598048,\"orig_timestamp\":\"2023-12-22T11:47:36.1833839Z\",\"timestamp\":\"2023-12-22T11:47:36.250Z\"}"
}
```

ここでちょっと問題発生です。意図的なのかどうか分かりませんが、Momentoが送ってくるJSONはテキストとして解釈されているようです。恐らくリクエストヘッダのContent-Type指定がないからだと思うのですが、デバッグでちょっと困りました。
テキストで渡ってくるbodyをJSONにしなければなりません。このような処理をするためにPostman Flowsでは[Evaluate](https://learning.postman.com/docs/postman-flows/reference/blocks-list/#evaluate)ブロックを用意しています。


このブロックの中では[FQL](https://learning.postman.com/docs/postman-flows/flows-query-language/introduction-to-fql/)という簡易言語で式を記述することができます。ここでは入力をJSONにする式を書きましょう。

1. rate_jsonの左から線をドラッグして、Evaluateブロックを選択し、Evaluateブロックの入力に`Start`を接続します。
2. Evaluateブロックに変数`input`を設定します。pathは空で構いません。これでbody全部がinput変数にセットされます。
3. FQL式を記載します。`$jsonParse(input)` です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/4c3cb056-f349-2416-cd95-ea334856890a.png)
4. rate_json側に戻って、rate_jsonに`text`を設定します。

これでOKです。

### テスト

デバッグの為に`Start`やAPIコールのErrorに[Log](https://learning.postman.com/docs/postman-flows/reference/blocks-list/#log-console)を設定します。Logに渡した入力はinputに出力されます。
全体のフローは下図のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/2fa5d3aa-aea1-c71e-3f43-90f029bce1e4.png)

左下にある`Webhook URL`の`Preview`を押して、APIのテスト画面を開きます。Bodyにテスト用のJSONを設定して実行してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/8b4195e5-f7ca-f51c-5fd3-7962d8346667.png)

Flowsの画面上からも実行結果が確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/5ded8c2e-192d-7470-29cc-2a56a9da640a.png)

また、コンソールにAPIコールとLOGの内容が出ているのも確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12495/d22eb11c-2966-2819-b7c9-97c868a62993.png)

### Publish

動作に問題なければWebhook URLの下にある`Publish`を押せばそのURLでデプロイされます。外部から叩いて確認してみましょう。

## まとめ

Postman Flowsを使うとデータ変換を伴うAPI呼び出しの連携をノーコードで作ることができます。また、Postmanプラットフォームで連携されているのでAPIテストと同様の変数が利用できるのも利便性が高いです。
今回は簡単な例でしたが、複雑な変換などもブロックやFQLで記述できるので、APIの連携開発に有効そうです。