20
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PORTAdvent Calendar 2020

Day 4

Web APIを実装しよう!!(Rails)

Last updated at Posted at 2020-12-04

はじめに

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

これでは、読みにくく、直感的ではありません。(何の機能なのかはなんとなくわかる気がする。。。)

dogstypeが同列に並んでいるので、階層的にわかりづらいですし、listallでは意味がほぼ同じなので、重複してます。
また、WebAPIを使用するユーザーが想定なので、URIにわざわざgetという単語を入れなくてもいいです。(後述のHTTPメソッドより)

なので、階層的にわかりやすく、シンプルにしたほうが使いやすそうですね。
以下のように修正します。

GET https://example.com/api/dogs/type/list

dogstypelistを取得できると直感的にわかるようになったと思います。(多分)

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メソッドの役割に沿って、記事の内容を更新するAPIPUTで行うようにしたほうがいいと思います。

HTTPレスポンス

APIを使用して返ってくるものにもしっかりと制約をつけ、インターフェースを統一しましょう。
(HEADは割愛させていただきます。すいません。)

ステータスコード

200とか、404とか、500とかのあれです。
ステータスコード単体でも意味を成しているので、HTTPメソッド同様、理解する必要があります。

よく目にするステータスコードは以下。

ステータスコード 意味
200 リクエストが成功した
201 リクエストが成功し、リソースが新規作成された
400 リクエストパラメータに不足・不備があった
401 アクセストークンが無効、認証されていない
404 リクエストされたリソースが存在しなかった
500 何らかのエラーがサーバー側で発生している

WebAPIの設計において、基本的には想像しうる状態を全て網羅しなくてはいけません。
網羅した上で、必要なステータスコードはなんなのかを考えていきましょう。

ボディ

ユーザーが実際に扱うデータのフォーマットや構造を考えて置く必要があります。

最近のWebAPIは、JSONで返すことが多いです。JSONのフォーマットにしたがっていけば問題なさそうですね。

レスポンスのボディは2種類あると思います。
リクエストが成功した場合リクエストが失敗した場合です。

リクエストが成功した場合

ユーザーが欲しい情報をJSONのフォーマットに沿って構造化していきます。

今回、個人的に意識したことは、階層構造です。

例として、ユーザー一覧を取得するAPIのJSONを考えます。
ユーザーを取得する際に、住所の情報も欲しいとします。

もし、住所の情報に、prefecturecityが欲しいとき、
以下のように階層ごとにグループ化することで、わかりやすく、データもまとまります。

users.json
{
  {
    "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/application.rb
config.api_only = true

を設定するとできるみたいです。(超便利!!)

ただ、今回は既存のプロジェクトの一部でAPIが実装されているので、上二つのやり方はできませんでした。

RailsのAPI実装の特徴は、APIモードではない、いつも通りのRailsとあまり変わらずに実装することができることです。

MVCのView層が使用されないくらいで、あとは同じ感じでコードを書けるので、楽にかけます。
ただ、APIではいつもよりも制限がかかっているので、仕様については理解しないといけないところがあります。

今回の実装で詰まった、ActionController::APIを簡単に説明します。

ActionController::API

APIモードにする際に、app/application.rbを以下のように変更する必要があります。

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を実装するときは、ドキュメントを読んで仕様を理解する必要があります。
便利な機能が揃っていて、ブラウザ向けアプリケーションの開発とあまり大差ないので楽にかける分、ブラウザ向けアプリケーションでできていたことができないことみたいな制約もあるので、注意が必要です。

参考

以下、参考にしたもの。

書籍

Web APIの設計

サイト

Rails API

Rails による API 専用アプリケーション

Rails における JSON のシリアライズと向き合う

Rails での JSON API 実装まとめ

API設計

翻訳: WebAPI 設計のベストプラクティス

RESTful APIとは何なのか

リソース指向アーキテクチャ(ROA)とは何なのか

RESTful APIのURI設計(エンドポイント設計)

RESTful APIのリソース設計

PORTについて

「世界中に、アタリマエとシアワセを。」 を目的に、就活や金融領域などの各産業領域に特化したメディアを展開しています。

PORT-INC. 公式ホームページ

20
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?