APIの特徴などを軽く抜粋(dysonさんメモ)
APIはインターフェイス=エンドポイントと思っておk
→要はURL(http://localhost3000/users/1など)決められたインターフェースに適合するようにリクエストを送ると、それに応じたレスポンスが返ってくる
外部の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
今回実装してみる要件
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ログインを叩くとこんな感じ
関連モデルのデータを返す
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" } ] } } } ] }
リクエストスペックの書き方も
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