読者です 読者をやめる 読者になる 読者になる

Rails で RESTful Web API をシンプルに保つ設計

Rails で RESTful な Web API を作る場合、色々な方法があると思います。

今回、私が API を作るときによくやっている設計をブログに書いてみます。

コード

説明するより、コードを見てもらった方が早いと思うので、コードを書く。

# config/routes.rb
namespace :api, defaults: { format: :json } do
  namespace :v1 do
    resource :user, only: :show
  end
end

# app/controllers/api/v1/users_controller.rb                                                                                                                                
module Api
  module V1
    class UsersController < ApplicationController
      before_action :doorkeeper_authorize!

      def show
        user = Api::V1::User.new(resource_owner_id: doorkeeper_token.resource_owner_id)
        render json: user
      end
    end
  end
end

# app/models/api/v1/user.rb
module Api
  module V1
    class User
      include ActiveModel::Model

      attr_accessor :resource_owner_id

      def as_json(_)
        { id: user.id }
      end

      private

      def user
        @user ||= ::User.find(resource_owner_id)
      end
    end
  end
end

# spec/requests/api/v1/users_spec.rb
require 'rails_helper'

RSpec.describe 'Api::V1::Users', type: :request do
  let(:json) { JSON.parse(response.body) }

  context 'without token' do
    before { get '/api/v1/user' }
    it { expect(response).to have_http_status 401 }
  end

  context 'with valid token' do
    let(:user) { create :user }
    let(:token) { create :doorkeeper_token, resource_owner_id: user.id }

    before { get '/api/v1/user', access_token: token.token }
    it { expect(response).to have_http_status 200 }
    it { expect(json).to include 'id' => user.id }
  end
end

説明

やっていることはとてもシンプルで User < ActiveRecord::Base とは別に、 Api::V1::User のモデルを作成しています。

JSONを生成するのに必要なパラメータを attr_accessor で定義しておき、それを initialize のときに渡します。あとは as_json で Hash を返すだけです。

この設計を使っている理由

  • Userメソッドを作らずに済む
    • User#as_json にすると V2 ができたときに辛くなるので…
  • Rails 標準の機能だけで作れる(gemを入れなくていい)
    • 学習コストが低い
  • DB を使わないような API も同じような形で書ける
    • Api::V1::Ping{"message":"pong"} を返す、とか
  • テストがしやすい
    • controller より、 model のテストの方が色々と楽
  • JSON の共通部分をapp/models/concerns に抽出しやすい

RESTful Web APIを簡単に作るgem

有名なのはこの2つでしょうか。

DSLを覚える学習コストが少し高そうだけど、GitHub の star も多いので便利なのかも。*1


API の設計方法を書いてみました。

RESTful Web API を作る際に、このブログが何かの参考になれば幸いです。

*1:今のところ ActiveModel で事足りているので使っていないけど