【超基本】API設計で気をつけたい事10選

[PONOS Advent Calendar 2023](https://qiita.com/advent-calendar/2023/ponos)の5日目の記事です。


# はじめに
昔はWEBサーバといえばHTMLをレンダリングすることが殆どでしたが、2010年代以降はゲームやスマホアプリなどを筆頭に、なんらかのAPIという形式でリソースを提供する、もしくは使用することが非常に多いんじゃないかと思います（体感）。

ということで今回は改めて個人的に **【とりあえずここは気になるというポイント１０選】** を挙げて見たいと思います。

正直挙げればキリがないので、インフラ面の話は一切省きソフトウェアとしての部分でチョイスしてみました。

# 対象者

これから初めてAPIを作るといったサーバエンジニアや、サーバには触れたことがないというエンジニアの方向けの内容です。

# １０選

## 1. 認証
まずはなんといっても認証です。
いろんなAPIを使っていると、それぞれ認証方法が異なったりするので真っ先に気になるポイントですし、自分で作る場合にも重要なポイントになってきます。

* HTTP認証
* アクセストークン

などが挙げられます。一般的には何らかの方法でリフレッシュトークンやアクセストークンを取得することが多いように思いますが、認証方法によって特性が異なってくる部分があります。
例えばJWTによってアクセストークンを発行するパターンでは、一般的にその検証はデータベースアクセスを伴わないためマイクロサービスなどで使い勝手がよいですが、特定のアクセストークンを即座に無効化することはできません。
このように特性にあった認証を選ぶことが求められます。

## 2. 権限（認可）
認証の次は権限です。

わかりやすいのはSNSなどのAPIで、

* 編集は自分しかできない
* 閲覧は友達までできる

とか、雑に書くとそういう部分です。

特定のアプリケーションに向けてAPIを構築する場合でも、少なからず何らかの権限的要素が含まれていると思います。

例えば

* 有効なアカウント
* 制限されたアカウント
* 削除されたアカウント
* 開発者アカウント

などです。
アカウントの種類によって使えるAPIに制限がかかるという事もありますし、やや特殊ケースでは開発者アカウントのみデバッグ用途にレスポンスに情報が追加されるなどのケースもありました。

## 3. 署名
使うかどうかに関わらず、署名も常に念頭にいれたほうがいいでしょう。
システムによっては、偽の中間サーバを利用した攻撃が問題になるケースがあると思います。
偽造したサーバからのレスポンスをクライアントに返すことで、クライアントがそれを信用して動作してしまうような問題です。

具体的な対処方法はここでは割愛します。

## 4. レスポンスコード
急に温度感が下がってしまうのですが、個人的にはこれも気になるケースがあります。

例えば

1. エラーコードのルールが一般的な規定と異なる
1. エラーコードの粒度が大きすぎる。もしくは小さすぎる。

です。

### エラーコードのルールが一般的な規定と異なる

例えばHTTPのレスポンスステータスを利用する場合です。
HTTPのステータスコードは一般的なお約束があるので、そこに齟齬があると解釈を間違ってしまいますが、変な使われ方をしていることを見かけることもあります。
例えば400番台はクライアント要因、500番台はサーバ要因のエラーコードになりますが、これが正しく適用されていないレスポンスが発生することがあると思います。

実装漏れであるケースや、バグであるケースの両方ありえると思いますが、個人的に一番よく見かけるのは本来 BadRequest になるべき入力値のミスなどが、サーバ側がハンドリングしきれずに500エラーで処理されるケースではないかと思います。
（異論あり）

500エラーは運用者にアラートが飛ばされるようなケースもあると思いますし、例えばGCPのAppEngineではサーバエラーとクライアントエラーをカウントして表示してくれたりしますが、これはHTTPステータスに依存します。
場合によってこれをクライアント側が意図的にひき起こせてしまうのは、なんとも心証が悪いなと感じます。

### エラーコードの粒度が大きすぎる。もしくは小さすぎる。

これはいくつかの懸念点がありますが、その一つにはサポートがあると思います。
エラーコードの粒度が大きすぎると、必要な問題の切り分けができないことになりますし、細かすぎても意味のない情報になってしまいます。

## 5. キャッシュ
一般的にはパフォーマンスのためにほぼ全てのAPIで何らかのキャッシュを使用していると思います。

* DBアクセスをキャッシュする
* レスポンスそのものをキャッシュする
* 何らかの計算結果をキャッシュする

などです。

言うまでもないことですが、キャッシュには **"結果が変わらない"** という特性があります。
そのため、**問題のあるデータをキャッシュしてしまった時、それをクリアできるか？** といった部分や、**意図せずユーザデータをキャッシュに含めてしまっていないか** という観点があります。

キャッシュが問題で違うユーザに間違ったデータを表示してしまうというような問題は、世の中を見渡すと過去実際起きています。
当たり前の話ですが、AさんのアカウントとBさんのアカウントで同時アクセスした時、正しく自分のレスポンスが得られるかどうかです。

## 6. 冪等性
これは **"何度同じ操作をしたとしても、同じ結果が得られる"** という意味です。

どういうAPIで冪等性を考慮すべきか、どこまでやるかというのはケースバイケースだと思いますが、一例としては **コインなどを消費して何かを抽選するシステム** はわかりやすいと思います。

APIは通信である以上、通信エラーになることもありえます。この時、クライアントにとって（ユーザから見て）は通信エラーでも、サーバ側にはリクエストが到達しており、処理が完了しているケースがあります。
この時何も考慮せずにリトライしてしまうと、ユーザの意図と異なり2度抽選を実行してしまうことになります。

もちろん、このエラーが発生した時はデータを取り直すことで、クライアントに抽選結果が反映されれば、最低限齟齬は起きないと思います。その一方でユーザから見た場合、これだと説明がなさすぎて何が起こったのかわからないことになり、「コインが減った！」と感じてしまうかもしれません。

冪等性が担保されたAPIであれば、通信をリトライすることで二重に抽選を実行することなく、前回の結果を得られます。

## 7. レースコンディション
これは複数同時にAPIアクセスを行うことで、データの競合が発生してしまうケースです。
主には排他処理が適切でなかったりする場合などに起こりえます。

特に初めてサーバサイドに触れると言うケースでは、同時アクセスに対する考慮が不十分になってしまうことが多いと思いますし、そうでなかったとしてもローカル環境で一回実行するだけではわからない部分でもあるので、見落としが発生することもあります。

テストでは（特に書き込みが発生するAPIであれば）必ずレースコンディションの確認をしたほうがいいでしょう。

## 8. パフォーマンスの劣化
システムは時間経過とともにパフォーマンスが劣化し、突然問題が生じるケースがあります。
簡単なパターンでは、

* メモリリーク（厳密にはリークでなかったとしても最大メモリを超える）
* ディスクフル
* DBへのデータ蓄積によってインデックス不足が判明する

などなどです。

もちろんAPIをリリースするにあたって、パフォーマンス面のテストは必ず実施すると思いますが、仮に48時間の運転試験をしたとして、その期間で問題が生じていない事はもちろんのことながら、ポイントとしてはその期間で右肩上がりになっているメトリクスはないかどうか（それが問題にならないか）という着眼点があります。
（一部の例外を除き）一般的に正常なメトリクスはアクセス数に比例するはずです。つまりアクセス数が下がれば、下がってくるはずです。そうでなく右肩に上がり続ける場合、それはなぜなのかがポイントだと思います。

## 9. アップデート（互換性）
多くの人が利用する公開されているAPIは後方互換などの運用がもちろん考慮されています。一般的にはバージョンごとにAPIが提供されており、同時に古いバージョンからの移行と、古いバージョンのEOLが定められている（もしくは予告されるなど）と思います。

自分たちのアプリのためなど限定的な目的でAPIを開発する場合、どこまで厳格に決めるのかという議論はもちろんあるかと思いますが、少なからずサーバを更新したら古いアプリが進行不能になりました、というような初歩的な失敗が起きないように注意することは最低限必要です。

## 10. メンテナンス
"超基本" ということで入れておきたいのがメンテナンスです。

基本的にはダウンタイムをできるだけ作らないよう設計すると思いますが、それでもAPIサーバはいくつかの理由でメンテナンスをする必要性が生じます。

1. ソフトウェアやハードウェアのアップデートのため
1. 何らかの障害が発生したため
1. 実行中のサービスに不具合が見つかり、その被害を直ちに停止するため

などです。

サーバ側は最悪後から対応をいれることが可能であったとしても、問題はAPIの利用者側です。APIの利用者が外部であったらもちろんですが、自分のアプリであったとしても、アプリは急に変われません。

サーバがメンテナンス状態に入った場合に、クライアント側がどのような挙動になるかはアプリケーションによるところではありますが、いずれにしてもメンテナンスという機能を設計に組み込んでおく必要があります。

# まとめ
ということで、１０選に絞ってあげてみましたが、いかがだったでしょうか？
「これもあるだろう」「私ならこっち」などなど、ご意見色々あろうかと思いますが、今回挙げたポイントは私が20年近くAPIを使ったり作ったりした経験の中で、多かれ少なかれ何らかの点でひっかかったことのある点ばかりを選びました。

皆様のAPIライフが円満でありますよう願っております。

それでは次回は[@kerimeka](https://qiita.com/kerimeka)さんです。