Help us understand the problem. What is going on with this article?

RailsでAPIを作成するために色々比較したので所感と実装方法のご紹介

More than 3 years have passed since last update.

はじめに

外部サービスと連携したい!アプリ開発をしたい!際にAPIサーバを作成することがあるかと思います。
今回はrubyを使った色々なAPIの実装方法をご紹介したいと思います。

登場人物

今回は下記の所感、実装方法をご紹介していきます。

リクエスト取得部分

  • Rails
  • Grape

レスポンス部分

  • Rails
  • RABL
  • Jbuilder

前提

今回は全ての検証で下記のテーブル、URL、出力になるようになっています。

Userテーブルの構成

id name addr1 addr2
1 田中 東京都千代田区 0-0-0
2 山田 東京都千代田区 1-1-1

リクエストURL
今回はバージョン管理出来るようにapi/v1/としました。

http://example.com/api/v1/users?addr1=東京都千代田区

レスポンス

[
    {
        id: 1,
        name: "田中",
        address: {
            addr1: "東京都千代田区",
            addr2: "0-0-0"
        }
    },
    {
        id: 2,
        name: "山田"
        address: {
            addr1: "東京都千代田区",
            addr2: "1-1-1"
        }
    },
]

リクエストの取得部分

Rails

所感

  • Railsの規則通りなのでRailsに慣れている人なら敷居が低い
  • Gemのインストール・設定が特にない

って感じですかね。試しにサクッとAPIを作るには向いていると思いました。

実装方法

controller配下にAPI用のディレクトリを作成する

users_controller_rb_-_canvath_-____Sites_canvath_canvath_.png

ルーティングの設定

通常のRailsと同様にroutesに設定をします。

config/routes.rb
# API用
namespace :api, {format: 'json'} do
  namespace :v1 do
    namespace :users do
      get "/" , :action => "index"
    end
  end
end

controllerの実装

controllerを実装していきます。
railsを書き慣れている人なら特に違和感はないかと思います。
params.permitにてリクエストパラメータのバリデーションをかけています。

app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
  def index
    @users = User.where("addr1 = ?", user_params[:addr1])
  end


  private
  # リクエストパラメータのバリデーション
  def user_params
    params.permit(:addr1)
  end
end

※ JSONとして出力する部分はレスポンス部分にて解説します。

grape

grapeとは

  • rubyで簡単にAPIを作成するためのDSL
  • 単体、もしくはRailsやSinatraと組み合わせ使う

所感

  • API専用のDSLなのでシンプルにかける
  • 若干初期設定が多い

って感じです。Railsに比べると大きめのプロジェクトに向いているのかなと感じました。

実装方法

Gemのインストール

Gemfile.rb
gem 'grape'
インストール
bundle install

routesの設定

config/routes.rb
mount API::Root => '/api'

grapeを読み込めるように設定

config/application.rb
# APIの読み込み
config.paths.add File.join('app', 'apis'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'apis', '*')]

# Grape+JBuilderを使うための設定
config.middleware.use(Rack::Config) do |env|
  env['api.tilt.root'] = Rails.root.join 'app', 'views', 'api'
end

ディレクトリの作成

grapeの場合は、app配下に下記図のように作成しました。
api_root_rb_-_canvath_-____Sites_canvath_canvath_.png

rootの設定

apis/api 配下にroot.rbを作成します。

apis/api/root.rb
module API
  class Root < Grape::API
    mount API::Ver1::Root
  end
end

apis/api/ver1 配下にroot.rbを作成します。

apis/api/ver1/root.rb
module API
  module Ver1
    class Root < Grape::API

      # これでdomain/api/v1でアクセス出来るようになる。
      version 'v1', using: :path
      format :json

      mount API::Ver1::Users

      # 404NotFoundの扱い
      route :any, '*path' do
        error! I18n.t('grape.errors.not_found'), 404
      end

    end
  end
end

API部分の実装

app/apis/api/ver1/user.rb
module API
  module Ver1
    class Users < Grape::API
      format :json

      # /api/v1/users/
      get '/users' do
        @users = User.where("addr1 = ?", user_params[:addr1])
      end

    end
  end
end

※ JSONとして出力する部分はレスポンス部分にて解説します。

レスポンス部分

続いてレスポンス(View)の部分をRailsRABLJbuilderを使った所感、実装方法をご紹介します。

Rails

所感

  • 簡単なものなら事足りるが、複雑なレスポンスは厳しそう。
  • controller内にレスポンスの処理が含まれるので、管理が疎かになりがち

って感じです。デモ等の簡単なものなら良いですが、複雑なものはちょっと厳しそうな印象です。

実装方法

先ほどリクエストの取得部分のRailsの項目で作成したコードを元にJSONして出力出来るようにします。

app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
  def index
    @users = User.where("addr1 = ?", user_params[:addr1])
    @users = @users.map do |user|
      {
          id: user.id,
           name: user.name,
          address:{
              addr1: user.addr1,
              addr2: user.addr2
          },
      }
    end
    render json: @users
  end


  private
  # リクエストパラメータのバリデーション
  def user_params
    params.permit(:addr1)
  end
end

RABL

RABLの特徴

  • Viewのテンプレートなのでcontrollerに記述しなくても良くなる
  • JSON以外のフォーマットにも対応している
  • API出力は基本的にモデルに紐づく
  • 他のRails、Jbuilderに比べて若干遅い

所感

  • 記述が独特なので慣れるまで少し時間がかかる
  • 特定のモデルに紐づくプロパティを表すのには便利そう

って感じです。モデルに紐づく出力はRABLがかなりサポートしてくれるが、紐付かないモノを複雑に表現するには苦手そう。
また、記述が独特なので、そこは慣れが必要そうです。

実装方法

bundle install

Gemfile
gem 'rabl'
shell
bundle install

コードの修正

こちらもリクエストの取得部分のRailsの項目で作成したコードを元にJSONして出力出来るようにします。

app/controllers/api/v1/users_controller.rb
class Api::V1::UsersController < ApplicationController
  def index
    @users = User.where("addr1 = ?", user_params[:addr1])
    render "api/ver1/users"
  end


  private
  def user_params
    params.permit(:addr1)
  end

end

RABLファイルを格納するディレクトリの作成

app/views/api/ver1/users.json.rablのディレクトリ構造にする

user_rb_-_canvath_-____Sites_canvath_canvath_.png

app/views/api/ver1/users.json.rabl
collection @users, :object_root => false
attribute :id => :id, :name => :name

node (:address) do |user|
  {
   addr1: user.addr1,
   addr2: user.addr2
  }
end

Jbuilder

Jbuilderの特徴

  • Viewのテンプレートなのでcontrollerに記述しなくても良くなる
  • 記述方法がシンプルで解りやすい
  • モデルに紐づかなくても簡単に扱える

所感

  • 複雑なフォーマットも表現しやすい
  • 条件式、モデルに紐付かないオブジェクトも簡単に扱えるので取り回しやすい

って感じです。RABLに比べると直感的で解りやすいと感じました。何よりモデルに紐付かない処理も表現しやすいのはかなり良いですね。

実装方法

Gemfile
gem 'grape'
gem 'grape-jbuilder'
shell
bundle install

API部分の修正

先ほどリクエストの取得部分のGrapeの項目で作成したコードを元にJSON出力出来るようにします。

app/apis/api/ver1/user.rb
module API
  module Ver1
    class Users < Grape::API
      format :json
      formatter :json, Grape::Formatter::Jbuilder

      # /api/v1/users/
      get '/users', jbuilder: 'ver1/users/user' do
        @users = User.where("addr1 = ?", user_params[:addr1])
      end

    end
  end
end

View部分をJbuilderで実装

json.array! @users do |user|
  json.id   user.id
  json.name user.name
  json.address {
    json.addr1   user.addr1
    json.addr2   user.addr2
  }
end

終わりに

API一つ作成するのでもかなり選択しがありました。
個人的に、簡単なAPIなら Rails単体、少し大きめのものならGreap + Jbuilder
が良さそうだと感じました。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away