黄昏酒場スコアボード作成③〜バックエンド開発環境整備とAPI定義

## はじめに

これは[②](https://qiita.com/wefmaika/items/df8f487079bedce6ce98)の続きです。

## バックエンド開発環境の作成

バックエンドはfastapiで動かす。fastapiはpythonで書くものなのでライブラリ関連は `pip` というPythonパッケージ管理ツールを使用して入れる。
これはDockerfileで `CMD` コマンドを使用して `pip install -r requirements.txt` と書いておき、母艦で `requirements.txt` に使用したいライブラリ一覧を記述し、 `CMD` コマンドの前に `COPY` コマンドを書いて母艦からコンテナに `requirements.txt` をコピーするようにして置けばDockerイメージをビルドする際に自動的に使用するライブラリが入ることになる。

しかしこのままだとVSCodeで編集する際に `requirements.txt` を記述したライブラリを使用しようとしたときに性的解析ツールが『そんなライブラリは知らん！！』とキレてくる。
何故Dockerfileに書いたのにVSCode上で怒られるかというと、あくまでコンテナ上にライブラリをインストールさせるようにしただけで、あくまでVSCodeで編集するのは母艦のpythonだからである。母艦にはライブラリを `pip install` していないので当然VSCode上の静的解析ツールはライブラリを認識しない。

脳筋であれば母艦上で `pip install` させれば解決することができるが、母艦の環境を汚すのは後々別のコンテナでホストを作成しようとしたときにライブラリが干渉する可能性がありダルい。
そこでpyenvというものを使用する。また、[pyenv-installer](https://github.com/pyenv/pyenv-installer)というものを使用すればpyenvインストール時にpyenv-virtualenvというものも一緒に付いてきて、project毎、さらにはディレクトリ毎にpythonの環境を分けることができて便利である。

## APIの定義

fastapiはHTTPリクエストのエンドポイントを定義することができる。
ざっくりHTTPについて説明すると `Hyper Text Transfer Protocol` の略で、サーバとブラウザ間でテキストベースに通信するための各コンピュータ間のお約束事(プロトコル)である。
HTTPには代表的には以下4種のメソッドがある。

- `GET`
    - 単にサーバから情報を得る
- `POST`
    - サーバに情報を送信する
- `PUT`
    - サーバにある情報を更新する(正直 `POST` と区別がつかない)
- `DELETE`
    - サーバにある情報を削除する

旧ロイヤルフレアでは、クライアントはcgiに対してリプレイをPOSTしたり削除パスワードを使用してDELETEし、それに対してcgiはhtmlファイルを生成して、記録を見たい人はcgiが作成したHTMLファイルをGETすることによって実現していた。
しかし今回はフロントエンドによってウェブページを動的に生成するものとした。よってクライアント側にはフロントエンドサーバから与えられたJavaScriptによる画面生成環境があり、JavaScriptに対してHTMLではなくJavaScriptが解釈できるようなフォーマットのテキストデータを送るだけで良くなり、画面生成の責任はない。
ここでフロントエンドの定義するJavaScriptがバックエンドに通信する出入口、及びそこから入出力されるデータを定義する必要が出てくる。このような出入り口を`API` (`Application Programmable Interface`)と呼ぶ。
今回のAPIは以下のように定義した。

- `GET /replays/sort={AA}&order={BB}&offset={CC}&limit={DD}`
    - リプレイの一覧を取得する
    - sort以降のパラメータで取得できる情報を調整できる
    - もしかしたらデプロイまでに変えるかも
- `GET /replays/{replay_id}`
    - `replay_id` に相当するリプレイ1件のメタデータを取得する
    - 今回は使用しないが便宜上作成する
    - 誰かが自分のページをもとに便利アプリ作りたければ使ってください
- `GET /replays/{replay_id}/file`
    - `replay_id` に相当するリプレイファイルをダウンロードする
- `POST /replays`
    - ユーザ名や投稿コメント、及び削除パスワードとともにリプレイをアップロードする
- `DELETE /replays/{replay_id}`
    - 削除パスワードとともにリクエストすると `replay_id` に相当するリプレイを削除できる
- `GET /cocktail` `GET /wine` `GET /sake` `GET /beer` `GET /alcohol`
    - `/teapot`というエンドポイントにリダイレクトする
- `GET /teapot`
    - RFC2324に従って `418 I'm a teapot` エラーを返す

ここまでで出入り口について定義できたが入出力されるデータフォーマットについて定義しなければならない。
特にHTTPはあくまでテキスト送受信の約束事なので通常であればリプレイファイルのようなバイナリファイルを送受信することはできない
そこでMIMEというものがRFC2045等で定義されており、これでテキスト送受信という建前を保ったままバイナリファイル等を送受信することができる。
MIMEで送れるフォーマットはIANAという組織で管理されており、 `application/form-data` や、 `application/json` 等がある。
jsonはキーバリューのデータ型で非常に扱いやすい。よって基本的に入出力はjsonで定義したがここで問題が起きた。
jsonのバリューにバイナリは入れられない[^1]ので `POST /replays` でjsonが使えない。
そこで全てのエンドポイントで入力を揃えるためにform-dataの方を使おうとしたが今度は `DELETE /replays/{replay_id}` にてform-data形式でパスワードを送信することができない。
というのもform-dataはHTTPが定義された初期からあり、HTTPのできた当初は『DELETEはあくまでリソースの削除であるから、サーバに送信するデータはない』と考えていたようで、form-dataの送信は禁止されているそう。
そこで一応ギリセーフである `application/json` でDELETE時のパスワードを送信することにした。

`GET /alcohol` などのエンドポイントは『黄昏酒場のスコアボードだし、お酒をGETリクエストしたらくれるのか？』と思わせて実はサーバ自体がティーポットで出来ているためお酒を渡すことは出来ないというエラーである[^2]。リターナー[^3]で我慢しろ。

## 次回

作る環境が出来、エンドポイントも定義できたので内部を実装していく。

[^1]: 文字列へBase64エンコードすれば入れることも技術的には可能だがそれやってる暇あったら `application/form-data` を使った方が良い。
[^2]: HTTP応答の `418 I'm a teapot` は1998年のエイプリルフールに発表されたRFC2324にて定義された、サーバにコーヒーを入れてもらうためのプロトコルである `HTCPCP` ( `Hyper Text Coffee Pot Control Protocol` )で制定されたエラー応答である。
このエラー応答は本来、サーバにコーヒーを入れてもらうように頼んだところ実はサーバはティーポットだったのでコーヒーは注げないというエラーである。
しかし今回はお酒に関するサービスを提供するサーバなので、コーヒーの部分をお酒に変えて実装した。RFC違反とみなされてもしょうがないがそもそも `HTCPCP` はなんちゃってプロトコルだから許してくれ。
[^3]:紅茶のお酒。