メインコンテンツまでスキップ

API

参考URL
実稼働環境におけるベスト・プラクティス: パフォーマンスと信頼性
Express×Helmetでウェブセキュリティを学ぶ
WebLogic Server のチューニングにおける重要推奨事項

冪等な処理を行うAPIの作り方

何度も呼び出されても変わらず返すためにはDBなどにプライマリーキーを設定する。
2度呼び出された場合はエラーを返すことができるようになる。

Image from Gyazo

APIモードが生まれた理由

かつてはページ遷移に伴ってAPIにアクセスを行っていた。しかし、昨今はページ読み込みとは別にタイミングで情報を取得する。 これによりやり取りするデータのサイズを小さくしたり、データのやり取りをタイミングを調整することでより良いユーザー体験を提供することが可能になるため。

モノシリックなAPIを作るべきではない

ポイントAPIなど、分離することで違うサービスにも提供ができる。
バックエンドサービスひとつで賄っていたのが、責務によって分けることが良い場合もある。

API種類

フロントエンドから直接機能されるAPIの開発には、いくつかの一般的な選択肢が存在する

  1. REST API
  2. GraphQL API
  3. WebSocket API

Google blog

API 設計: gRPC、OpenAPI、REST の概要と、それらを使用するタイミングを理解する

上記のAPI種類が決まったら

  • セキュアな通信か
  • 認証は?
  • 承認は?
  • 監視、可観測性、ログ

API設計に関するまとめ

参考URL
REST_APIのコツ

  • LSUDs Large Set of Unknown Developers(不特定多数のユーザーに提供するAPI)

  • SSKDs Small Set of Unknown Developers(特定のシステムのみで利用する専用のAPI)

  • HATEOS Hypermedia As The Engine Of Application State(リソース同士に関連性のあるAPIのこと)

APIリクエスト(POST/PUT)のcontent-type

  • application/x-www-form-urlencoded
    body部分にkey-value形式で送信したいデータを格納する。

  • application/json(こっちのが主流)
    body部分にJSON形式で送信したいデータを格納する。

APIレスポンスのcontent-typeとフォーマット

  • application/json(JSON-RPC)

  • application/hal+json(HAL)

  • application/vnd.api+json(JSON API) これがParrotだとbackendが返す。

  • application/vnd.collection+json(Collection JSON)

レスポンス圧縮する必要性

gzip圧縮方法について徹底解説


リクエストをparseする

列挙する

  • クライアントからのjson parse(POSTのメッセージ部分)
  • 通常のフォームリクエストのボディ(application/x-www-form-urlencoded)

通常のフォームリクエストのボディ

Content-Typeがapplication/x-www-form-urlencodedのケース 各フレームワークで違いはあれど、parseできる準備をしないといけない。


JWT

JWT=ステートレス"から一歩踏み出すための考え方


ここからは書籍Webを支える技術を参考にする

3章 Webのアーキテクチャスタイル

実際のシステムは具体的なアーキテクチャを持っている。そのアーキテクチャを設計するときにただ闇雲に作っているのではなくアーキテクチャ設計の指針、作法、流儀、つまりアーキテクチャスタイルを適用する システムのアーキテクチャを決定する際の羅針盤となるのがアーキテクチャスタイル

アーキテクチャ: ブラウザ/サーバ/プロキシ/HTTP/URI/HTML ↓ アーキテクチャスタイル(マクロ)アーキテクチャパターン: REST/MVC(Model-View-Controller)/パイプフィルター/イベントシステム

デザインパターン

デザインパターン(マイクロアーキテクチャパターン)といい、アーキテクチャスタイルよりも粒度の小さいクラスの設計様式を指す

healthCheck

参考URL

healthCheckはAPIの通信ができるかといった外形監視と呼ばれる。

api&DBまでを確認するのが基本っぽい。 しかし、クラウドなど(AWS)でデフォルト確認などできるのであればDBまでは不要かも。

運用しているYELL BANKというサービスは、以前公開した『ECS(Fargate)でコンテナアプリケーションを動かすための設定情報の扱い方』という記事でも紹介した通り、コンテナー上で動作することを前提としたアプリケーションとして機能提供しています。 コンテナー 内で動かすアプリケーションにおいても、外形監視は重要と感じていますが、

パスワード保存

平文は基本NG dbにパスワードが保存される時によく使われる(平文で保存はだめ) dbに平文を保存してはいけない理由

もし外部へパスワードの「ハッシュ値」が漏洩してしまった場合、「レインボーテーブル攻撃」や「総当り攻撃(ブルートフォース攻撃)」で「ハッシュ値」からパスワードを推測されてしまう危険性があります。 そのため、パスワードは「ソルト」と「ストレッチング」を実施した形でハッシュ値に変換します。 この「ソルト」や「ストレッチング」を考慮した形でハッシュ値へと変換してくれるのが、bcryptです。

ソルトは個別に分けるべき

query

これらのqueryは基本的にAND条件が多いです。例えばYouTube APIとかでもqueryは色々ありますが、複数指定した場合はそれらのANDでの結果が返ってきますね。 ORにする場合は複雑なcombinatorとかをqueryで実現する実装が必要だったりして面倒なのですが、とりあえず今回の件ではどちらも考慮したものでOKです 矛盾する2つのqueryを指定されたらエラーを返す、みたいなAPIもよくあります。(例えば日付の絶対指定と相対指定を両方提供しているAPIで両方値が入っていたケースなど)

json apiのフォーマット

Web APIにはJSONベースのフォーマットを使おう

Web APIを作るとき、JSONのデータ構造をどうするか悩んだことはあると思う。 それを決めてくれる。

  • JSON API(parrotで使用)
  • HAL

JSON API

チェックリスト

  • レスポンスヘッダーにセキュリティを追加したか
  • dbに
  • レスポンスは決めたか?(json or xml)

path tips

  • APIにおける /users/{userId}/me について どっちを採用するか 参考URL

/v1/me/images でアクセスした場合は非公開の画像が帰ってくるようにする。 /v1/users/{userId} では非公開の情報は(たとえ本人によるアクセスでも)帰ってこない。 /v1/users/{userId} で本人によるアクセスの場合のみ非公開の情報を返す。

レートリミット

※クラウド時代に関してはアプリケーションレベルでレートリミットをかける必要はないかもしれない。 参考URL レートリミットは、一定の時間内にプロダクトを操作できる回数を制限するもの。

クラウドやホスティングサーバで運用する場合は、クラウド/ホスティング側にそのような機能が提供されていることもあると思います。 が、もしそのような機能が提供されていない条件下でこのような要件が生じた場合アプリケーションの実装としてリクエスト制限を用意する必要が出てくるかもしれない

ただし、この制限は「1インスタンスごとの制限」である点に注意が必要です。クラウド的な言い方だと「1コンテナ」あたりでこの制限が有効になりますが、複数インスタンスで運用した場合、例えば可用性を高める目的で3つのコンテナを起動して運用した場合は、事実上設定値の3倍のリクエスト処理を受け付けることになる、という点に注意が必要です。

データリミット

利用者が通常使用するのに必要な量を超えるデータを取得できないようにするもの。 jsonのpostが10kbにするとか

apiの権限設計

参考URL

WebAPI

webでのapiをまとめる

GraphQL

一度のアクセスで関連情報をすべて取得する。 所感 これTypeORMのRepositoryパターンとめちゃくちゃ合うな。 db取得をeagerにして一度で取得して一度で返す。 これはかなり効率がいい。

REST API

たびたび必要な情報を取得するために何度から別のapiを叩く必要がある。

汎用的なapi処理に必要な処理

バリデーション

外部から送られてくるデータを精査&整形する。

Sanitization(サニタイゼーション) 和訳:消毒

整形する部分。 ノイズがないことを確認する。 理解として、空白のトリミングやHTMLエスケープなどのサニタイズを適用。 ※重要 サニタイズによりリクエストが変更されることに注意。 これは、req.body.textが値とともに送信された場合 Hello world :>) サニタイズ後にその値が Hello world :> になること。

Web API

Web APIを設計する際にさまざまなものを作成するかと思うがそれを記載する。

GET/POSTmethodしか使えない。

HTML4.0 <form> タグはmethod属性に追加できるのがGETとPOSTだけ。 初期のHTML5のドラフトではPUTやDELETEも使える仕様が盛り込まれたが、結局削除となった。

しかしapiがGETとPOSTにしか対応していないは使いにくい。 そういったケースにそなえ、よく利用されているケースはAPI側でGETとPOST以外のメソッドをPOSTを使って表現する方法が2つある。

どちらもメソッドとしてはPOSTを利用して、メタ情報として本当はこのメソッドが使いたいというふうにする

  1. X-HTTP-Method-OverrideというHTTPリクエストヘッダーにDELETE or PUT入れる こっちのが利用しやすい。

  2. _methodパラメーター railsで使用されている。

WebAPIの通信

異なるマシン上で、動作するサービス間で情報をやり取りする手法について。 さまざまな情報システムの設計において、サービス間での情報のやり取りをどのように行うかというのは古くからある課題の1つ。 こういったサービス間での通信技術としてはさまざまなものがあるが、その中でも広く使われている手法の1つにRPC(Remote Procedure Call、直訳すると「遠隔手続き呼び出し」)がある。

RPC(Remote Procedure Call)

RPCはいわゆるクライアント−サーバー型の通信プロトコルであり、サーバー上で実装されている関数(Procedure、プロシージャ)をクライアントからの呼び出しに応じて実行する技術。 クライアントはサーバーに対し実行する処理を指定するパラメーターや引数として与えるデータを送信し、それに対しサーバーはパラメーターに応じた処理を実行してその結果をクライアントに返す、というのがRPCの基本的な流れ。 RPCは古くからある考え方であり、インターネットが普及する以前より存在していた。

変化する部分

一方で、どのようにクライアントからリクエストを送信するかや、どのようなフォーマットでデータをやり取りするかについては時代に応じて変化しており、さまざまな実装が存在する。 近年のWebベースのシステムにおいてはHTTP/HTTPSベースでサーバー・クライアント間のやり取りを行い、またその際のデータフォーマートにはXMLを利用する「XML-RPC」や、同じくHTTP/HTTPSベースでデータフォーマットにJSONを利用する「JSON-RPC」といったものが多く使われている。

XML-RPCやJSON-RPCは、広く普及しているHTTP/HTTPSやXML、JSONといった技術を利用するため導入しやすいという利点がある。一方で、これらは基本的にテキストベースで情報をやり取りするためデータの転送効率が悪く、またバイナリデータを扱いにくいという問題があった。また、HTTP/HTTPSを使用しているため比較的長い期間で散発的にデータをやり取りしにくかったり、転送効率の面でもオーバーヘッドが存在したりする。

解決する通信プロトコル(gRPC)

gRPCについて

上記の問題を解決するのが、gRPC

gRPCの歴史と特徴

gRPCはGoogleが開発した、RPC技術がベースとなっている。 Googleでは多数のコンポーネントを組み合わせてサービスを実現する、いわゆる「マイクロサービスアーキテクチャ」でシステムを構築していることで知られるが、これらのサービス間での通信を行うためにgRPCの前身となる「Stubby」と呼ばれる技術が開発された。ただ、このStubbyはGoogleのインフラ以外での利用は想定しておらず、独自の仕様が多かったそうだ。その後Stubbyで使用されていた技術とコンセプトが近いHTTP/2などの技術が登場したことから、GoogleはStubbyにこれらの技術を取り入れてオープン化することを決め、それがgRPCとなった。gRPCはオープンソースで公開されており、現在はLinux Foundation傘下のCloud Native Computing Foundation(CNCF)によって開発が進められている

gRPCの技術的概要

gRPCではほかのRPCと同様、クライアントがサーバーに対しリクエストを送信し、サーバーはそれに応じた処理を実行してその結果を返すという、クライアント−サーバーモデルを採用している。そして、gRPCは特定の言語やプラットフォームに依存しないように設計されており、さまざまな言語向けにクライアントやサーバーを実装するためのライブラリが公開されている。そのため、クライアントとサーバーが異なる言語で実装されていても、問題なく通信を行うことができるようになっている。