はじめに
PORT Advent Calendar 2020 の4日目の記事です!
はじめまして! PORT新卒エンジニアのしゅんいち(shxun6934) です!
業務でのとあるタスクにて、Railsを使用してWebAPIを実装することになりました。
今までは、WebAPI(QiitaとかTwitterとかお世話になりました。)を使用する側でしたが、WebAPIを作成する側になったので、いろいろわからないところが多かったです。
簡単にですが、そのタスクに臨むにあたって学んだこと、意識していたことを皆さんに共有しておきたいと思います。
そもそも、APIって何?
API(Application Programming Interface) は、アプリケーションとアプリケーション、人、組織をつなげてくれるものです。
APIと言っても種類はいくつかあります。
(例:Twitterから「いいね」の情報を取得するためのもの、iOSアプリでカメラの機能を使用するためのもの、画像の色を反転させるためのもの、などなど。。。)
一般的に言われている「API」は、WebAPIを指すことが多いです。
WebAPIは、ネットワークを使用して他のアプリケーションと通信する APIのことです。
RESTful API
RESTの原則に従ったAPI。
ここで思うことは、「RESTって何???」
簡単に言うと、RESTは複数のソフトウェアを連携させるための設計原則の考え方のことです。
RESTの原則は以下の4つ。
URIを通して提供できる情報が表現できていること = アドレス可能性
インターフェースが統一されていること = 統一インターフェース
やり取りする情報で全てを完結させること = ステートレス性
情報の内部に別の情報や状態へのリンクを含めることができること = 接続性
これらに従って設計すれば、使いやすくAPIになります。
では、実際にWebAPIで意識することを見ていきます。
URI
WebAPIなので、URIは必須になります。
ここでは、REST原則のアドレス可能性が鍵になります。
URIはユーザーが実際に入力するものなので、わかりやすく、どんな機能を持つURIがひと目でわかるようにしたほうがベストです。
例として、犬の種別の一覧を取得するAPIを設計します。
GET https://example.com/api/get-dogs-type-all-list
これでは、読みにくく、直感的ではありません。(何の機能なのかはなんとなくわかる気がする。。。)
dogs
とtype
が同列に並んでいるので、階層的にわかりづらいですし、list
とall
では意味がほぼ同じなので、重複してます。
また、WebAPIを使用するユーザーが想定なので、URIにわざわざget
という単語を入れなくてもいいです。(後述のHTTPメソッドより)
なので、階層的にわかりやすく、シンプルにしたほうが使いやすそうですね。
以下のように修正します。
GET https://example.com/api/dogs/type/list
dogs
のtype
のlist
を取得できると直感的にわかるようになったと思います。(多分)
HTTPメソッド
REST原則の統一インターフェースに一番密な要素です。
標準化されたHTTPメソッドの個々の役割を理解して、それらをどのAPIでも適用するものです。
基本的なHTTPメソッドは、以下。
HTTPメソッド | 役割 |
---|---|
GET | リソースを取得する、検索する |
POST | リソースを作成する、追加する |
PUT | リソースのデータを更新する |
DELET | リソースを削除する |
もし、POSTで「リソースのデータを更新する」役割をも持たせたとして、
記事を投稿するAPI
と記事の内容を更新するAPI
があったとします。
2つのAPIを以下のように設計します。
POST /api/articles
POST /api/articles/:article_id
上記の設計だと、同じアプリケーションに「リソースを作成する」POST
と「リソースのデータを更新する」POST
が存在してしまいます。
これでは、POST
に関して同じ意味をなさなくなるので、インターフェースが統一されていないことになり、使いづらいAPIになってしまいます。
この例では、HTTPメソッドの役割に沿って、記事の内容を更新するAPI
はPUTで行うようにしたほうがいいと思います。
HTTPレスポンス
APIを使用して返ってくるものにもしっかりと制約をつけ、インターフェースを統一しましょう。
(HEADは割愛させていただきます。すいません。)
ステータスコード
200
とか、404
とか、500
とかのあれです。
ステータスコード単体でも意味を成しているので、HTTPメソッド同様、理解する必要があります。
よく目にするステータスコードは以下。
ステータスコード | 意味 |
---|---|
200 | リクエストが成功した |
201 | リクエストが成功し、リソースが新規作成された |
400 | リクエストパラメータに不足・不備があった |
401 | アクセストークンが無効、認証されていない |
404 | リクエストされたリソースが存在しなかった |
500 | 何らかのエラーがサーバー側で発生している |
WebAPIの設計において、基本的には想像しうる状態を全て網羅しなくてはいけません。
網羅した上で、必要なステータスコードはなんなのかを考えていきましょう。
ボディ
ユーザーが実際に扱うデータのフォーマットや構造を考えて置く必要があります。
最近のWebAPIは、JSONで返すことが多いです。JSON
のフォーマットにしたがっていけば問題なさそうですね。
レスポンスのボディは2種類あると思います。
リクエストが成功した場合
とリクエストが失敗した場合
です。
リクエストが成功した場合
ユーザーが欲しい情報をJSON
のフォーマットに沿って構造化していきます。
今回、個人的に意識したことは、階層構造です。
例として、ユーザー一覧を取得するAPIのJSON
を考えます。
ユーザーを取得する際に、住所の情報も欲しいとします。
もし、住所の情報に、prefecture
とcity
が欲しいとき、
以下のように階層ごとにグループ化することで、わかりやすく、データもまとまります。
{
{
"id": 1
"first_name": "AAAA",
"middle_name": null,
"last_name": "BBBB",
"sex": "man",
// 住所として一つのグループにする。
"address": {
"prefecture": "東京都",
"city": "新宿区"
},
"favorite_tags": ["Ruby", "Rails", "WebAPI"]
"url": "https://example.com/users/1"
},
{
"id": 2
"first_name": "CCCC",
"middle_name": "1111",
"last_name": "DDDD",
"sex": "woman",
// 同列だとわかりづらい
"prefecture": "東京都"
"city": "渋谷区"
"favorite_tags": []
"url": "https://example.com/users/2"
},
}
リクエストが失敗した場合
ユーザーに何のエラーなのか、どうして起きたのかを正しく提供しなくてはいけません。
ユーザー一覧を取得するAPIのパラメーターに、ソートをかけるとします。(例:favorites_tagsで"Ruby"を持っているユーザーの一覧)
ここで間違えて、"Ruby"ではなく、数字を入れてしまったときのレスポンスは、
以下のようにする必要があると思います。
https://example.com/api/users?favorites_tags=65847
{
"message": "Invalid Request",
"errors": [
{
"type": "bad_type",
"message": "favorites_tags is String, is not Integer"
}
]
}
エラーが複数存在する場合は、グループ化して、わかりやすくするのも一つの手ですね。
Railsでは、、、
Webアプリを作成するために最適なRails
でも当然、WebAPIの実装はできます。
RailsでのAPI実装
Railsには、APIモードなるものが存在していて、プロジェクトを新規作成する場合は、
$ rails new my_api --api
でAPIモードになります。
また、既存のアプリをAPI専用にしたい場合は、config/application.rb
にて
config.api_only = true
を設定するとできるみたいです。(超便利!!)
ただ、今回は既存のプロジェクトの一部でAPIが実装されているので、上二つのやり方はできませんでした。
RailsのAPI実装の特徴は、APIモードではない、いつも通りのRailsとあまり変わらずに実装することができることです。
MVC
のView層が使用されないくらいで、あとは同じ感じでコードを書けるので、楽にかけます。
ただ、APIではいつもよりも制限がかかっているので、仕様については理解しないといけないところがあります。
今回の実装で詰まった、ActionController::API
を簡単に説明します。
ActionController::API
APIモードにする際に、app/application.rb
を以下のように変更する必要があります。
# ActionController::Base → ActionController::APIに変更する。
class ApplicationController < ActionController::API
end
これでAPI専用になるのですが、、、
このActionController::API
は、ActionController::Base
をAPI専用にしたものであることを認識しないといけませんでした。
上記でいつも通りに開発できると言いましたが、厳密には少し違います。
このActionController::APIのリファレンスのこの部分。
Keep in mind that templates are not going to be rendered
APIモードでも、render
メソッドでhtmlやxmlのフォーマットを指定すれば、読み出せます。しかし、template
の読み出しはできないため、jsonをviewとして扱うことが結構難しくなっています。
まとめ
APIを実装する上では、ユーザー目線に立つことが何よりも大事だと思いました。
実際に使う人の目線に立って、使いづらくないか、わかりやす構造になっているかを気にしてAPIを組み立てていく必要があると思います。
RailsでWebAPIを実装するときは、ドキュメントを読んで仕様を理解する必要があります。
便利な機能が揃っていて、ブラウザ向けアプリケーションの開発とあまり大差ないので楽にかける分、ブラウザ向けアプリケーションでできていたことができないことみたいな制約もあるので、注意が必要です。
参考
以下、参考にしたもの。
書籍
サイト
Rails API
API設計
PORTについて
「世界中に、アタリマエとシアワセを。」 を目的に、就活や金融領域などの各産業領域に特化したメディアを展開しています。