yuw27b’s blog

技術メモと雑記

RESTful APIで長い検索クエリを扱う

Webアプリケーション用のRESTfulっぽいAPI*1を作っていて、複雑な検索クエリを受け取る必要が出てきたときのメモです。

悩み

RESTfulの原則に従うと、検索クエリを投げて結果の一覧を取得する、というリクエストは、
「リソースの取得」なので、GETを使うべきです。
/api/entries/?q=xxx とか
/api/entries/?created=20161129&status=active
みたいなURIになりますよね。

こんな感じで検索キーワードが1つ2つなら良かったのですが、
今回作っていたのが限定されたユーザー向けのアプリで、多少検索に時間がかかってもいいから複雑なクエリを投げたい、というのが要件でした。
こんなふうに検索条件を組み合わせられるようになっていました。
f:id:yuw27b:20161202233544p:plain

問題は、
①検索条件がいくつ来るか分からない
②「q=xxx」の形式では「equals」「not equals」のところが渡せない
という点です。

まず①で、GETのクエリパラメータが長くなる==URIが長くなるので、ブラウザ〜ファイアウォール〜サーバまでのどこかで制限される可能性がありそうです。
さすがにそこまで長くは・・・とは思うものの、理論上は無制限に検索ワードを指定できるので、「たぶん大丈夫」ではやっぱりダメですよね。

②も、そのままJSONを文字列にエンコードするとか、「?key__equals__value&…」とか考えるものの、やはりクエリが長くなってきます。
そもそもフロント側でクエリを文字列に変換し、それをサーバ側でまたパースする、というのがあまりイケてないような気もします。

POST使っちゃダメ?

GETが厳しいとなるとPOSTで、と思うものの、
RESTful APIでは、POSTはリソースの新規作成なので、検索クエリを投げて良いのだろうか、と悩むし、
そもそも「/api/entries/」に対するPOSTリクエストは、当然"entry"の新規作成用に使ってしまっています。

解決策:検索用エンドポイントを用意してPOSTする

「/api/entries/search」というURIにPOSTでJSONを投げることにしました。
参考にしたのはこれ↓です。
rest - Design RESTful query API with a long list of query parameters - Stack Overflow

レスポンスのステータスは迷いましたが「201 Created」を返しています。
上のリンク先にもあるように「検索結果というリソースを生成している」と捉えています。まあ200でも良さそうですが。

新たな悩み

検索条件が設定されているかどうかをフロント側で判別して、あれば「POST /api/entries/search」、なければ「GET /api/entries」にリクエストをかけているのですけど、
もう全部POSTに統一のほうがいいのだろうか・・・?


ひとまず、そこまでREST原理主義でなくてもいいかと思いこんな感じにしましたが、何かもっといい方法があれば知りたいです!

*1:最近、業務で作っているWebアプリケーション専用のAPIなので、API単体で一般に公開するような性質のものではないです。Ruby(Sinatra)+MySQLで、フロントエンドからのリクエストにJSONを返しています。