SQLiteのDBをWebAPIにするnpx cliツールを作った

## 序

フロントエンドの実験中や、開発の初期の初期、PoCの段階などで、**DBを使いたいんだけどバックエンドを書くのめんどくさい**ってこと、ないでしょうか。私は割とあります。
そこで、SQLiteのDBを即席WebAPIにする開発用ツールを作りました。

#### npm

https://www.npmjs.com/package/apifi-sqlite

#### GitHub

https://github.com/kedama-t/apifi-sqlite

## Usage

- `npx`で一撃です。

```sh
npx apifi-sqlite ./hoge.db 3000
```

- このコマンドを実行すると、`hoge.db`のテーブルごとに、`http://localhost:3000/`以下にエンドポイントが生えます。
- ポートは省略可能で、デフォルトは`3000`です。
- `http://localhost:3000/`にアクセスすると、APIのリストが~~雑に~~出ます。
    - ほんとは[Swagger](https://swagger.io/)みたいに出せればよかったのですが、そこまでするモチベーションがなかった。

## サンプルDBをAPI化してみる。

- なんでもいいですが、私がよく使うSQLiteのサンプルDBはこれ。

https://www.sqlitetutorial.net/sqlite-sample-database/

```sh
npx apifi-sqlite ./chinook.db
```

<details><summary>するとこんな感じでAPIが生えます。</summary>

```sh
Server is running on http://localhost:3000
- GET : http://localhost:3000/albums
- DELETE : http://localhost:3000/albums
- POST : http://localhost:3000/albums
- PUT : http://localhost:3000/albums
- GET : http://localhost:3000/sqlite_sequence
- DELETE : http://localhost:3000/sqlite_sequence
- POST : http://localhost:3000/sqlite_sequence
- PUT : http://localhost:3000/sqlite_sequence
- GET : http://localhost:3000/artists
- DELETE : http://localhost:3000/artists
- POST : http://localhost:3000/artists
- PUT : http://localhost:3000/artists
- GET : http://localhost:3000/customers
- DELETE : http://localhost:3000/customers
- POST : http://localhost:3000/customers
- PUT : http://localhost:3000/customers
- GET : http://localhost:3000/employees
- DELETE : http://localhost:3000/employees
- POST : http://localhost:3000/employees
- PUT : http://localhost:3000/employees
- GET : http://localhost:3000/genres
- DELETE : http://localhost:3000/genres
- POST : http://localhost:3000/genres
- PUT : http://localhost:3000/genres
- GET : http://localhost:3000/invoices
- DELETE : http://localhost:3000/invoices
- POST : http://localhost:3000/invoices
- PUT : http://localhost:3000/invoices
- GET : http://localhost:3000/invoice_items
- DELETE : http://localhost:3000/invoice_items
- POST : http://localhost:3000/invoice_items
- PUT : http://localhost:3000/invoice_items
- GET : http://localhost:3000/media_types
- DELETE : http://localhost:3000/media_types
- POST : http://localhost:3000/media_types
- PUT : http://localhost:3000/media_types
- GET : http://localhost:3000/playlists
- DELETE : http://localhost:3000/playlists
- POST : http://localhost:3000/playlists
- PUT : http://localhost:3000/playlists
- GET : http://localhost:3000/playlist_track
- DELETE : http://localhost:3000/playlist_track
- POST : http://localhost:3000/playlist_track
- PUT : http://localhost:3000/playlist_track
- GET : http://localhost:3000/tracks
- DELETE : http://localhost:3000/tracks
- POST : http://localhost:3000/tracks
- PUT : http://localhost:3000/tracks
- GET : http://localhost:3000/sqlite_stat1
- DELETE : http://localhost:3000/sqlite_stat1
- POST : http://localhost:3000/sqlite_stat1
- PUT : http://localhost:3000/sqlite_stat1
```
</details>

### `GET`
- 生えたエンドポイントをGETで叩くと、そのテーブルの中身をJSON配列で返します。

:::note info
- GET - `http://localhost:3000/albums`

#### SQL

```SQL
SELECT * FROM albums
```

#### レスポンス

```json
[
  {"AlbumId":1,"Title":"For Those About To Rock We Salute You","ArtistId":1},
  {"AlbumId":2,"Title":"Balls to the Wall","ArtistId":2},
... // 長いので省略
]
```
:::

- `Where`句での絞り込みは、クエリ文字列で渡します。

:::note info
- GET - `http://localhost:3000/albums?AlbumId=1`

#### SQL

```SQL
SELECT * FROM albums WHERE AlbumId = 1
```

#### レスポンス

```json
[
  {"AlbumId":1,"Title":"For Those About To Rock We Salute You","ArtistId":1}
]
```
:::

- クエリ文字列は、`?{field},{operator}={value}&...`の形式で指定します。
  - 条件は複数使えますが、全て`and`になります。
    - `or`に対応しようとするとガチャガチャするかなと思って、一旦割り切りました。

:::note info
- GET - `http://localhost:3000/albums?AlbumId,in=1,2,3,4,5&Title,like=the`

#### SQL

```SQL
SELECT * FROM albums WHERE AlbumId in (1,2,3,4,5) AND Title Like '%the%'
```

#### レスポンス

```json
[
  {"AlbumId":2,"Title":"Balls to the Wall","ArtistId":2},
  {"AlbumId":4,"Title":"Let There Be Rock","ArtistId":1}
]
```
:::

- 演算子は`eq(equals)`、`ne(not equals)`、`in`、`notin`、`like`、`notlike`、`gt(greater than)`、`ge(greater equal)`、`lt(lesser than)`、`le(lesser equal)`が指定できます。
  - [README.md#Operators](https://www.npmjs.com/package/apifi-sqlite#operators)も参照されたい。

### `POST`
- 生えたエンドポイントをPOSTで叩くと、そのテーブルに`INSERT`を流します。
- リクエストbodyに`{field: value}`のようなJSON形式で、データを渡してあげます。

:::note info
- POST - `http://localhost:3000/albums`

```json
// content-type: application/json
{
    "AlbumId" : 400,
    "Title" : "Albmus for Testing",
    "ArtistId" : 1
}
```

#### SQL

```SQL
INSERT INTO albums (AlbumId,Title,ArtistId) VALUES (400, 'Albums for Testing', 1)
```

#### レスポンス
- 新しいIDが帰ってきます。

```json
400
```
:::

### `PUT`
- 生えたエンドポイントをPUTで叩くと、そのテーブルに`UPDATE`を流します。
- これも、リクエストbodyに　`{field: value}`の形で、データを渡してあげます。
- ちゃんとクエリ文字列で`Where`句を書いてあげないと、**全部上書き**するので注意が必要。

:::note info
- PUT - `http://localhost:3000/albums?AlbumId=400`

```json
// content-type: application/json
{
  "Title" : "Albums for Testing 2"
}
```

#### SQL

```SQL
UPDATE albums SET Title = 'Albmus for Testing 2' WHERE AlbumId = 400
```

#### レスポンス
- 更新されたデータの件数が返ってきます。

```json
1
```
:::

### `DELETE`
- 生えたエンドポイントをDELETEで叩くと、そのテーブルに`DELETE`を流します。
- ちゃんとクエリ文字列で`Where`句を書いてあげないと、**全部消します**ので注意。

:::note info
- DELETE - `http://localhost:3000/albums?AlbumId=400`

#### SQL

```SQL
DELETE FROM albums WHERE AlbumId = 400
```

#### レスポンス
- 削除されたデータの件数が返ってきます。

```json
1
```
:::

## おわりに

- フロントエンドの実験をする時に、めんどくさくてGitHubのAPI叩いたりしてたのですが、もうちょっと実態に即したデータで試したいなぁ、ということで作りました。
    - 機能が限定的だったり、エラーハンドリングが雑でなんのエラーだかわからなかったり、まだまだ改善の余地はありますが、自分の開発用ツールとしてはまずまず便利に使えるものができました。
    - 「欲しいものはある程度自分で作れる」のがプログラマーという職業のいいところです。

## Appendix:依存ライブラリについてのメモ
- [Express](https://expressjs.com/ja/) - Web APIの構築
- [EJS](https://ejs.co/) - APIリストを表示するために使用
- [Jest](https://jestjs.io/) - テスト
- [supertest](https://github.com/ladjs/supertest) - Express.jsのテスト用