ryota21silvaの技術ブログ

Funna(ふんな)の技術ブログ

これまで学んだ技術の備忘録。未来の自分が救われることを信じて

【Rails】RailsでAPIを叩く基本編

APIの特徴などを軽く抜粋(dysonさんメモ)

  • APIインターフェイス=エンドポイントと思っておk
    →要はURL(http://localhost3000/users/1など)

  • APIを叩く=HTTPリクエストを送る

  • 決められたインターフェースに適合するようにリクエストを送ると、それに応じたレスポンスが返ってくる

  • 外部のAPIを使う
    →あるパラメータをあるエンドポイントに送ると、あるレスポンスが返ってくる。

  • データベースに情報を持っている
    →それを皆に使ってもらえるようにAPIを作る

  • 決められたインターフェースに適合するように=こういう条件を満たすように

  • JSON形式のデータを返すことが主流 render json: hoge

jsonのserializerとは?

jsonを生成する仕組みのこと。
→この生成したJSONのデータをレスポンスとして返す。

ActiveRecordは ActiveModel::Serializationをinclude している。なので、以下のようにrender jsonと、そのまま記述できる。

module Api
  module V1
    class ArticlesController < BaseController
      def index
        articles = Article.all
        render json: articles
      end
    end
  end
end
  • PostmanでAPIを叩くとJSONレスポンスが返ってきていることが分かる。
    f:id:ryota21silva:20200726122953p:plain
    PostmanでAPIを叩いた

今回実装してみる要件

fast_jsonapiというserializerを使って、 記事一覧のjsonレスポンスを返す。

  • Articleモデル。Userモデルと一対多の関係。
(app/models/article.rb)
class Article < ApplicationRecord
  belongs_to :user
  has_many :comments, dependent: :destroy

  has_one_attached :eye_catch
  enum status: { draft: 0, in_review: 10, published: 20, archived: 30 }
end
  • Articleモデルのシリアライザーを作る
$ be rails g serializer Article title contents status                                    
Running via Spring preloader in process 21798
      create  app/serializers/article_serializer.rb
(app/serializers/article_serializer.rb)
class ArticleSerializer
  include FastJsonapi::ObjectSerializer
  attributes :title, :contents, :status
  belongs_to :user
end
(app/controllers/api/v1/articles_controller.rb)
# frozen_string_literal: true

module Api
  module V1
    class ArticlesController < BaseController
      def index
        articles = Article.all
        json_string = ArticleSerializer.new(articles).serialized_json
        # 上と同じ
        # json_string = ArticleSerializer.new(articles).serializable_hash.to_json
        render json: json_string
      end
    end
  end
end

PostmanでAPIを叩いてみると、ちゃんとデータが取れた。

{
    "data": [
        {
            "id": "1",
            "type": "article",
            "attributes": {
                "title": "title-1",
                "contents": "contents-1",
                "status": "draft"
            },
            "relationships": {
                "user": {
                    "data": {
                        "id": "1",
                        "type": "user"
                    }
                }
            }
        },
        {
            "id": "2",
            "type": "article",
            "attributes": {
                "title": "title-2",
                "contents": "contents-2",
                "status": "draft"
            },
            "relationships": {
                "user": {
                    "data": {
                        "id": "2",
                        "type": "user"
                    }
                }
            }
        },
...
......
........

Userログインを叩くとこんな感じ f:id:ryota21silva:20200726123531p:plain


関連モデルのデータを返す

  • ArticleSerializer.new(@article).serialized_jsonの場合
def show
        # options = { include: [:user, :'user.name', :'user.email'] }
        json_string = ArticleSerializer.new(@article).serialized_json
        render json: json_string
      end

以下のようなJSONレスポンスが返ってくる。記事に関連付いたユーザーのidも返ってくる。

{
    "data": {
        "id": "1",
        "type": "article",
        "attributes": {
            "title": "title-1",
            "contents": "contents-1",
            "status": "draft"
        },
        "relationships": {
            "user": {
                "data": {
                    "id": "1",
                    "type": "user"
                }
            }
        }
    }
}
  • options = { include: %i[user user.name user.email] }ArticleSerializer.new(@article, options).serialized_jsonの場合
def show
  # options = { include: [:user, :'user.name', :'user.email'] }
  options = { include: %i[user user.name user.email] }
  json_string = ArticleSerializer.new(@article, options).serialized_json
  render json: json_string
end

記事に関連付いたユーザーのレコードも返ってくる。

    "data": {
        "id": "1",
        "type": "article",
        "attributes": {
            "title": "title-1",
            "contents": "contents-1",
            "status": "draft"
        },
        "relationships": {
            "user": {
                "data": {
                    "id": "1",
                    "type": "user"
                }
            }
        }
    },
    "included": [
        {
            "id": "1",
            "type": "user",
            "attributes": {
                "name": "user-1",
                "email": "user-1@example.com"
            },
            "relationships": {
                "articles": {
                    "data": [
                        {
                            "id": "1",
                            "type": "article"
                        }
                    ]
                }
            }
        }
    ]
}

qiita.com

リクエストスペックの書き方も

require 'rails_helper'
RSpec.describe "Api::V1::Articles", type: :request do
  let!(:user) { create(:user) }
  
  describe "GET /api/v1/article/:id" do
    let!(:article) { create(:article, user: user) }
    it "掲示板詳細を返す" do
      get api_v1_article_path(article.id), headers: {
        'CONTENT_TYPE': 'application/json',
        'ACCEPT': 'application/json'
      }
      json = JSON.parse(response.body)
      expect(response).to be_successful
      expect(response).to have_http_status(200)
      expect(json["data"]["id"].to_i).to eq(article.id)
      # to_iでid='1'という文字列を数値に変換する
      # digを使う
    # expect(json["data"]["relationships"]["user"]["data"]["id"].to_i).to eq(article.user_id)
      expect((json.dig("data", "relationships", "user", "data", "id")).to_i).to eq(article.user_id)
    end
  end