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

# はじめに
外部サービスと連携したい！アプリ開発をしたい！際に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](https://qiita-image-store.s3.amazonaws.com/0/49347/7cbc33ef-66b6-4207-a85e-463e309be4b7.png "users_controller_rb_-_canvath_-____Sites_canvath_canvath_.png")


#### ルーティングの設定
通常のRailsと同様にroutesに設定をします。

```rb: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**にてリクエストパラメータのバリデーションをかけています。

```rb: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のインストール

```rb:Gemfile.rb
gem 'grape'
```

```bash:インストール
bundle install
```

####routesの設定

```rb:config/routes.rb
mount API::Root => '/api'
```
#### grapeを読み込めるように設定

```rb: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](https://qiita-image-store.s3.amazonaws.com/0/49347/7e554a51-cfb7-9c6e-f16a-219f307b6e08.png "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``を作成します。

```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部分の実装

```rb: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)の部分を**Rails**、**RABL**、**Jbuilder**を使った所感、実装方法をご紹介します。

## Rails

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

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

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

```rb: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

```rb:Gemfile
gem 'rabl'
```

```bash:shell
bundle install
```

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

```rb: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](https://qiita-image-store.s3.amazonaws.com/0/49347/bf6c88e5-92c6-ee35-56b2-d4cbbbd79d83.png)


```rb: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に比べると直感的で解りやすいと感じました。何よりモデルに紐付かない処理も表現しやすいのはかなり良いですね。

### 実装方法

```rb:Gemfile
gem 'grape'
gem 'grape-jbuilder'
```

```bash:shell
bundle install
```


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

```rb: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で実装

```rb:
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
が良さそうだと感じました。