Brunoを利用してAPIコレクションをファイルで管理

- API開発においてPostmanのようなテストやドキュメントに便利なクライアントツールを日々利用しています。
- 今回はそれらの中から[Bruno](https://www.usebruno.com/)という最近リリースされたAPIクライアントツールを利用した記録をします。

## 概要
- オープンソースのAPIクライアントツール。
- コレクションをファイルシステム上にテキストで管理するため、バージョン管理システム(Git等)を用いることが可能。
- [Bru](https://www.usebruno.com/bru)というシンプルなマークアップ言語を利用。

## 環境
- 今回Brunoを試すにあたって利用した環境は以下です。
    - Mac OS 13.4.1
    - deno 1.37.1
    - Bruno 0.21.0

## 手順

### サンプルAPI作成
- Brunoを利用するにあたり、対象となるAPIが必要です。
- 今回は[Deno](https://deno.com/)を利用して以下のようなGETとPOSTのみの簡単な「**TODO API**」を作成します。
    - `GET /todos`: タスク取得
    - `POST /todos`: タスク作成
- まず、以下のコマンドで任意のディレクトリ及びファイルを作成します。

```shell
mkdir ~/work/bruno-sample
cd ~/work/bruno-sample
touch app.ts
```

- 作成後、以下の内容を`app.ts`に記述します。

```typescript
import {
  Application,
  isHttpError,
  Router,
} from "https://deno.land/x/oak/mod.ts";

const app = new Application();
const router = new Router();

type Todos = {
  title: string;
  isDone: boolean;
};
const todos: Todos[] = [
  {
    title: "task1",
    isDone: false,
  },
  {
    title: "task2",
    isDone: false,
  },
];

router.get("/todos", (ctx) => {
  ctx.response.body = todos;
});

router.post("/todos", async (ctx) => {
  const body = ctx.request.body({ type: "form" });
  const value = await body.value;
  const title = value.get("title")?.trim();
  if (!title) return;
  todos.push({
    title: title,
    isDone: false,
  });
  ctx.response.body = todos;
});

app.use(async (context, next) => {
  try {
    await next();
  } catch (err) {
    if (isHttpError(err)) {
      context.response.status = err.status;
    } else {
      context.response.status = 500;
    }
    context.response.body = { error: err.message };
    context.response.type = "json";
  }
});
app.use(router.routes());
app.use(router.allowedMethods());

await app.listen({ port: 8000 });
```

- 記述後、以下のコマンドで実行して`localhost:8000/todos`にアクセスして簡易的なAPIが作成できていることを確認してください。

```shell
deno run --allow-net app.ts
```

### インストール
- 対象APIが用意できたのでBrunoをインストールします。
- BrunoではデスクトップアプリやCLI等が用意されていますが、今回はデスクトップアプリを利用します。
- 以下のリンクから自身の環境に応じてダウンロードしてください。
    - https://www.usebruno.com/downloads

- ダウンロードして起動後、以下の画面になっていることを確認します。

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/a54c95ff-8511-4032-93b6-07b85261b297)

### コレクション作成
- インストール完了後、Brunoにコレクションを作成していきます。
- まずホーム画面の「**Create Collection**」を押下して、必要情報を入力して作成してください。
    - **Name**: コレクション名
    - **Location**: 対象ディレクトリ
    - **Folder Name**: 対象ディレクトリ内に作成するコレクションディレクトリ名

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/2b839bd8-d65f-41a5-89c7-381e2dba2a04)

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/9c6686c6-24a5-4654-996f-a32c69f479a1)

### リクエスト作成

- 次にリクエスト群を作成していきます。
- 以下のように「New Request」ボタンを押下して必要情報を入力して「**GET・POST**」リクエストを作成してください。

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/213ce7c0-551a-4def-ae69-a8d03a655c48)

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/0bc61023-6a0a-4359-90a1-a91d7397f194)

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/8e7f20f1-31fa-47f2-988c-e8157024b5eb)

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/c8c5f71e-e95e-4616-9964-d2ee7362c3da)

### 環境変数設定

- 次に環境変数を設定します。
- 上記のリクエスト作成時にURL入力欄に「**{{baseUrl}}**」を利用しましたが、こちらはBrunoで扱える環境変数であり、共通テンプレートのような使い方が可能です。
- 以下のように画面右上にある「**Configure**」を押下して「**Create Environment**」で必要情報を入力して環境変数を設定してください。

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/7951c550-91dc-443b-a26d-f25f4dc40469)

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/455913f7-adcc-4187-9601-9c04539f2423)

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/9d1f1720-da55-4d85-9d75-df4bf550d7ec)

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/3c1867c6-c937-4428-99fc-fc759fb71d5b)

### 実行確認
- リクエスト及び環境変数が設定できたら、実行して以下のように結果が表示されることを確認します。

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/0358547c-994e-4cdf-9a0c-853b4baf807f)

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/8deec59d-d1e5-4b81-afd1-973cd8e46c2d)

### ファイル確認
- 次にBrunoの特徴であるコレクションのファイル管理を見ていきます。
- Brunoで指定したディレクトリを再度確認すると自動的に以下のようなフォルダ及びファイルが作成されています。

```shell
$ pwd
$HOME/work/bruno-sample

$ ls
app.ts		collections

$ ls collections
CREATE TODO.bru	GET TODOS.bru	bruno.json	environments

$ ls collections/environments
Local.bru
```

- また以下のように中身も確認すると、GUIで設定した**リクエスト名**や**環境変数名**が確認できます。

```shell
# 設定ファイル
$ cat collections/bruno.json

{
  "version": "1",
  "name": "todo-api",
  "type": "collection"
}

# リクエスト内容ファイル
$ cat collections/CREATE\ TODO.bru

meta {
  name: CREATE TODO
  type: http
  seq: 2
}

post {
  url: {{baseUrl}}/todos
  body: formUrlEncoded
  auth: none
}

body:form-urlencoded {
  title: task3
}

# 環境変数格納ファイル
$ cat collections/environments/Local.bru

vars {
  baseUrl: http://localhost:8000
}
```

- このようにBrunoではコレクションをファイルとして自動的に保存するので、バージョン管理ができてGithub等のシステムを利用して共有する流れとなっているそうです。

### テスト作成
- 最後にBrunoがサポートしているAPI自動テストスクリプトを作成します。
- BrunoではJavaScriptを利用してAPIのリクエストやレスポンスのテストを記述することが可能です。
- 例として以下のように任意のリクエスト欄の「Tests」タブ内の入力欄に「**レスポンスのステータスが正常か**」を確認するコードを記述して実行して結果を確認します。

```javascript
test("status must be 200", () => {
  expect(res.getStatus()).to.eql(200);
});
```

![image](https://github.com/Kamekure-Maisuke/xta/assets/44114228/debdcf36-2807-47cf-9fb2-8977fe7c3324)


- 実行後、上書き保存して以下で実際にファイルにも反映されていることを確認します。

```shell
$ cat collections/GET\ TODOS.bru

meta {
  name: GET TODOS
  type: http
  seq: 1
}

get {
  url: {{baseUrl}}/todos
  body: none
  auth: none
}

tests {
  test("status must be 200", () => {
    expect(res.getStatus()).to.eql(200);
  });
}
```

- Brunoのテストスクリプトで利用できるAPIリファレンスは[こちら](https://docs.usebruno.com/scripting/javascript-reference.html)です。

## まとめ
- コレクションをシンプルな構文でファイル管理できるので、視認性や共有のしやすさが良いと感じました。
- 日が浅いながら便利な機能が豊富にあるので、他も試しながら利用を検討します。

## 参考
- [公式サイト](https://www.usebruno.com/)
- [Github](https://github.com/usebruno/bruno)
- [ビジョン](https://github.com/usebruno/bruno/discussions/269)