window.pipedriveLeadboosterConfig={です。 ベース:'leadbooster-chat.pipedrive.com'、 companyId:11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2、 } ;(function () { var w = window もし (w.LeadBooster) {なら console.warn('LeadBooster already exists') } else { w.LeadBooster = { {. q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: 関数 (n) { { this.q.push({ t: 'o', n: n, h: h }) this.q.push({ t: 't', n: n }) }, } } })() REST的APIにサブリソースを含める - The Codest
The Codest
  • 会社概要
  • サービス
    • ソフトウェア開発
      • フロントエンド開発
      • バックエンド開発
    • Staff Augmentation
      • フロントエンド開発者
      • バックエンド開発者
      • データエンジニア
      • クラウドエンジニア
      • QAエンジニア
      • その他
    • アドバイザリー
      • 監査&コンサルティング
  • 産業
    • フィンテック&バンキング
    • E-commerce
    • アドテック
    • ヘルステック
    • 製造業
    • 物流
    • 自動車
    • アイオーティー
  • 価値
    • CEO
    • CTO
    • デリバリー・マネージャー
  • チーム
  • Case Studies
  • ノウハウ
    • ブログ
    • ミートアップ
    • ウェビナー
    • リソース
採用情報 連絡先
  • 会社概要
  • サービス
    • ソフトウェア開発
      • フロントエンド開発
      • バックエンド開発
    • Staff Augmentation
      • フロントエンド開発者
      • バックエンド開発者
      • データエンジニア
      • クラウドエンジニア
      • QAエンジニア
      • その他
    • アドバイザリー
      • 監査&コンサルティング
  • 価値
    • CEO
    • CTO
    • デリバリー・マネージャー
  • チーム
  • Case Studies
  • ノウハウ
    • ブログ
    • ミートアップ
    • ウェビナー
    • リソース
採用情報 連絡先
戻る矢印 戻る
2022-03-22
ソフトウェア開発

REST的APIにサブリソースを含める

The Codest

クシシュトフ・ブシェビッチ

シニア Software Engineer

著者データを含む(または含まない)本をリストアップする本棚アプリを作ります。

どうするんだ?

著者データを含む(または含まない)本をリストアップする本棚アプリを作ります。単一の 1TP63インデックス アクションといくつかのシード。このアプリは、どのようにしてユーザーにアクションをコントロールさせることができるかを示すサンプルアプリとなる。 REST的APIのサブリソース.

「受け入れ基準

  • ユーザーは本をリストアップすることができます。
  • ユーザーはパスできる を含む。 クエリ・パラメータを使用して、関連するリソース (著者).
  • を含む。 クエリーパラメーターは、カンマで区切られた文字列で、ネストされたリソースを表します。
  • どのリソースがどのアクションにインクルード可能かを定義する定数が必要だ。

ツール

を使用する。 ブループリンター をシリアライザとして使用することができます。これはrailsの標準ツールセットに追加する唯一のgemです。

アプリ

サンプルアプリを作ってみよう。テスト・フレームワークは私たちの範囲外なので追加しない。

新しい本棚

今すぐ作成する 著者 モデルである:

モデル作者名:文字列
#=> active_recordを呼び出す
#=> db/migrate/20211224084524_create_authors.rb を作成する。
#=>作成 app/models/author.rb

そして 書籍:

rails g モデル book author:references title:string
# => active_recordを呼び出す
# => create db/migrate/20211224084614_create_books.rb
# => app/models/book.rbの作成

種が必要だ:

# db/seeds.rb

dumas = Author.create(名前: 'Alexandre Dumas')
lewis = Author.create(名前: 'C.S. Lewis')
martin = Author.create(名前: 'ロバート・C・マーティン')

Book.create(author: dumas, title: '三銃士')
Book.create(author: lewis, title: 'ライオンと魔女と衣装だんす')
Book.create(著者:マーティン、タイトル:『クリーン・コード』)

これでマイグレーションを実行し、データベースをシードする準備ができた:

rails db:migrate && rails db:seed

追加しよう has_many の本のために 著者 モデルである:

# app/models/author.rb

class Author < ApplicationRecord
  has_many :books
終了

データを返すコントローラを書きましょう。ここでは API という名前空間があるので、まずは抑揚に頭文字を追加してみよう:

# config/initializers/inflections.rb

ActiveSupport::Inflector.inflections(:en) do |inflect|.
  inflect.acronym 'API'
終了

では、シリアライザーを ジェムファイル:

# Gemfile に追加

gem 'blueprinter'

そしてもちろん、それをインストールする:

バンドルインストール

そうすれば、設計図を作ることができる:

# app/blueprints/author_blueprint.rb

class AuthorBlueprint < ブループリンター::ベース
  識別子 :id

  フィールド :name
終了
# app/blueprints/book_blueprint.rb

class BookBlueprint < ブループリンター::ベース
  識別子 :id

  フィールド :title

  関連 :author, ブループリント:著者ブループリント
終了

のベースコントローラーを追加する。 API:

# app/controllers/api/v1/base_controller.rb

モジュール API
  モジュール V1
    class BaseController < ActionController::API
    end
  終了
終わり

そして、私たちの草案 ブックスコントローラー:

# app/controllers/api/v1/books_controller.rb

モジュール API
  モジュール V1
    class BooksController < BaseController
      def index
        Books = Book.all

        jsonをレンダリングするBookBlueprint.render(books)
      終了
    終了
  終了
終了

もちろん、ルーティングも定義しなければならない:

# config/routes.rb

Rails.application.routes.draw do
  名前空間 :api do
    名前空間 :v1 do
      リソース :books, only: :index
    end
  end
終了

これまでやってきたことをテストしてみよう:

レール 
curl http://localhost:3000/api/v1/books

# => [{"id":1, "author":{"id":1, "name": "Alexandre Dumas"}, "title": "三銃士"},{"id":2, "author":{"id":2, "name": "C.id": "3", "author":"{"id": "3", "name": "ロバート・C・マーティン"}, "title": "クリーン" コード"}]

データは問題ないようだが、ログはどうなのか?

#リクエストログ(n+1)

GET "/api/v1/books" for 127.0.0.1 at 2021-12-24 10:19:40 +0100を開始しました。
API::V1::BooksController#indexによる処理 */*として
  ブックのロード (0.1ms) SELECT "books".* FROM "books"
  ↳ app/controllers/api/v1/books_controller.rb:7:in `index'
  著者ロード (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ?LIMIT ?  [["id", 1], ["LIMIT", 1] ]。
  ↳ app/controllers/api/v1/books_controller.rb:7:in `index'.
  著者ロード (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ?LIMIT ?  [["id", 2], ["LIMIT", 1] ]。
  ↳ app/controllers/api/v1/books_controller.rb:7:in `index'.
  著者のロード (0.1ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" = ?LIMIT ?  [["id", 3], ["LIMIT", 1] ]。
  ↳ app/controllers/api/v1/books_controller.rb:7:in `index'.
6ms で 200 OK を完了 (Views: 0.1ms | ActiveRecord: 0.4ms | Allocations: 3134)

シリアライザーにアソシエーションを使用することで、次のようになりました。 n+1 問題である。このエンドポイントに何をリクエストするかをユーザーがコントロールできるようにすることで、この問題を解消したい。つまり、ブックだけをロードすることも、includesパラメータを渡して著者も取得することもできるようにしなければなりません。 n+1.

この定数を定義して、ユーザーがどのような本のassocを books1TP63インデックス アクションだ:

# lib/constants/books/includes.rb

モジュール Constants
  モジュール Books
    モジュール Includes
      ALLOWED = { (許可される)
        インデックス%i[(本)
          著者
        フリーズ
      フリーズ
    終了
  終了
終了

次に、空のオブジェクト定数用の名前空間を定義する:

# lib/constants/empty.rb

モジュール定数
  モジュールEmpty
    HASH = {}.freeze
  終了
終了

そして、これがインクルードを許可するためのメイン・サービスだ。このコードはとてもわかりやすいと思います。 マジック にのみ割り当てられる。 #default_resources_key そして #デフォルトの目的.これらのメソッドは、railsのコントローラでパラメータのみを渡すpermit includeを呼び出せるように定義されています。出力は 真の 許可されたインクルージョンごとに。

# app/services/permit_includes.rb

require 'constants/empty'
require 'constants/books/includes'.

クラス PermitIncludes
  空 = 定数::空

  COMMA = ','
  スラッシュ = '/'

  INCLUDES_FORMAT = /A[a-z]+(,[a-z]+)*z/.freeze
  allowed_includes = {(許可されたインクルード
    書籍:定数::ブック::インクルード::ALLOWED
  .フリーズ

  def call(params, resources: default_resources_key(params), purpose: default_purpose(params))
    includes_sent?(params)でなければEmpty::HASHを返す。
    includes_valid?(params)でなければEmpty::HASHを返す

    requested_includes = parse_includes(params)
    allowed_includes = filter_includes(requested_includes, resources, purpose)

    allowed_includes.index_with(true)
  終了

  プライベート

  def default_resources_key(params)
    raise(ArgumentError, 'params :controller key must be a string') unless params[:controller].is_a?(String)

    params[:controller].split(SLASH).last&.to_sym
  終了

  def default_purpose(params)
    raise(ArgumentError, 'params :action key must be a string') unless params[:action].is_a?(String)

    params[:action].to_sym
  終了

  def includes_sent?(params)
    params.key?(:includes)
  end

  def includes_valid?(params)
    params[:includes].is_a?(String)でなければfalseを返す

    params[:includes].match?(INCLUDES_FORMAT)
  終了

  def parse_includes(params)
    params[:includes].split(COMMA).map(&:to_sym)
  end

  def filter_includes(requested_includes, resources_key, purpose)
    requested_includes & ALLOWED_INCLUDES[resources_key][purpose].
  end
エンド

ここで、インクルードをロードするためにキーを使用し、インクルードのハッシュ自体をシリアライザーに渡す必要がある:

# app/controllers/api/v1/books_controller.rb

モジュール API
  モジュール V1
    class BooksController < BaseController
      def index
        includes = PermitIncludes.new.call(params)
        books = Book.includes(includes.keys).all

        jsonをレンダリングする:BookBlueprint.render(books, includes: includes)
      終了
    終了
  終了
終了

そして、このようにシリアライザーを調整しなければならない:

# app/blueprints/book_blueprint.rb
class BookBlueprint (_field_name, _book, options) { オプション[:includes
                         options[:includes] && options[:includes][:author].
                       }
終了

もう一度テストしてみよう:

レール
カール http://localhost:3000/api/v1/books
# => [{"id":1, "title": "The Three Musketeers"},{"id":2, "title": "The Lion, the Witch and the Wardrobe"},{"id":3, "title": "Clean Code"}]。
#のリクエストログ(書籍のみ読み込みます)
GET "/api/v1/books" for ::1 at 2021-12-24 10:33:41 +0100を開始しました。
API::V1::BooksController#indexで*/*として処理。
   (0.1ms) SELECT sqlite_version(*)
  ↳ app/controllers/api/v1/books_controller.rb:8:in `index'.
  ブックのロード (0.1ms) SELECT "books".* FROM "books"
  ↳ app/controllers/api/v1/books_controller.rb:8:in `index'
9ms で 200 OK を完了 (Views: 0.1ms | ActiveRecord: 0.9ms | Allocations: 4548)

よし、まだincludesを通過していないので、著者のいない本だけを手に入れた。では、リクエストしてみよう:

curl 'http://localhost:3000/api/v1/books?includes=author'
id":2, "author":{"id":2, "name": "C.S.ルイス"}, "title": "ライオンと魔女と衣装だんす" },{"id":3, "author":{"id":3, "name": "Robert C. Martin"}, "title": "クリーン・コード" }]% 
#のリクエストログ(n+1を排除)

GET "/api/v1/books?includes=author "を開始しました。
としてAPI::V1::BooksController#indexで処理する。
  パラメータ{"includes"=>"author"}
  ブックのロード (0.1ms) SELECT "books".* FROM "books"
  ↳ app/controllers/api/v1/books_controller.rb:8:in `index'
  著者のロード (0.2ms) SELECT "authors".* FROM "authors" WHERE "authors". "id" IN (?, ?, ?) [["id", 1], ["id", 2], ["id", 3]].
  ↳ app/controllers/api/v1/books_controller.rb:8:in `index'.
完了 200 OK in 17ms (Views: 0.1ms | ActiveRecord: 0.7ms | Allocations: 7373)

クールだ!我々は、協会をロードし、排除した n+1 問題です。このサービスはどんなリソースにも使うことができる。私たちがしたいことは、許可された定数を適切な形式で追加し、それを PermitIncludes::ALLOWED_INCLUDES.

関連付けを含むと多くのメモリを "消費 "する可能性があるため、ページネーションと併用する(そして注意する)必要があることを忘れてはならない。

関連記事

フィンテック

Rubyの5つの活用例

Rubyで何ができるのだろうと思ったことはないだろうか。まあ、可能性は無限大でしょうが、多少なりとも知られている事例についてお話しできれば幸いです。

The Codest
パヴェル・ムジンスキー Software Engineer
ソフトウェア開発

RubyとGraphQLにおけるポリモーフィズム

この記事では、GraphQLにおけるポリモーフィズムの使い方を紹介する。しかしその前に、ポリモーフィズムとGraphQLが何であるかを思い出しておく価値がある。

ルカシュ・ブレジッチ
E-commerce

サイバーセキュリティのジレンマデータ漏洩

クリスマス前の駆け込み需要が本格化している。愛する人への贈り物を求めて、人々はますますオンラインショップを "襲撃 "することを厭わなくなっている。

The Codest
ヤクブ・ヤクボヴィッチ CTO & 共同創設者
ソフトウェア開発

アクティブレコードを使ったゼロからのシンプルなRubyアプリケーション

MVCは、アプリケーションの責任を分担して動きやすくするデザインパターンです。Railsは慣例的にこのデザインパターンに従っています。

The Codest
ダミアン・ワトロバ Software Engineer

ナレッジベースを購読して、IT部門の専門知識を常に最新の状態に保ちましょう。

    会社概要

    The Codest - ポーランドに技術拠点を持つ国際的なソフトウェア開発会社。

    イギリス - 本社

    • オフィス 303B, 182-184 High Street North E6 2JA
      イギリス、ロンドン

    ポーランド - ローカル・テック・ハブ

    • ファブリチュナ・オフィスパーク、アレハ
      ポコジュ18、31-564クラクフ
    • ブレイン・エンバシー, コンストルクトースカ
      11, 02-673 Warsaw, Poland

      The Codest

    • ホーム
    • 会社概要
    • サービス
    • Case Studies
    • ノウハウ
    • 採用情報
    • 辞書

      サービス

    • アドバイザリー
    • ソフトウェア開発
    • バックエンド開発
    • フロントエンド開発
    • Staff Augmentation
    • バックエンド開発者
    • クラウドエンジニア
    • データエンジニア
    • その他
    • QAエンジニア

      リソース

    • 外部ソフトウェア開発パートナーとの協力に関する事実と神話
    • 米国から欧州へ:アメリカの新興企業がヨーロッパへの移転を決断する理由
    • テックオフショア開発ハブの比較:テックオフショア ヨーロッパ(ポーランド)、ASEAN(フィリピン)、ユーラシア(トルコ)
    • CTOとCIOの課題は?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • ウェブサイト利用規約

    著作権 © 2025 by The Codest。無断複写・転載を禁じます。

    jaJapanese
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek jaJapanese