0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RailsのAlbaとkaminariを使って、Albaのresponseに kaminariのpagination meta情報を動的に組み込む

0
Last updated at Posted at 2026-02-23

概要

Alba(JSON シリアライザ)のレスポンスに、kaminari のページネーション情報(currentPage, totalPages 等)を meta として動的に含める方法。コレクション取得時のみ meta を付与し、単体取得時は空のhashを返す。調べた感じ、ありそうな感じでなかったので。

結論

要素 内容
meta の定義場所 PostResource(各リソース)の meta ブロック
meta 生成ロジック BaseResource に共通メソッドとして定義
動的判定 object.is_a?(Enumerable) でコレクション/単体を判定

実装

BaseResource に共通メソッドを定義

# app/resources/base_resource.rb
class BaseResource
  include Alba::Resource
  transform_keys :lower_camel

  # TODO: ここだけ、lowerCamelなのはなんとかしたい
  # albaのtransform_keysでは処理されないみたい
  # 普通にcontroller外でも使えるっぽい。
  def paginated_meta(collection)
    {
      currentPage: collection.current_page,
      totalPages: collection.total_pages,
      totalCount: collection.total_count,
      nextPage: collection.next_page,
      prevPage: collection.prev_page
    }
  end
end
  • transform_keys :lower_camel により、レスポンスのキーは currentPage, totalPages 等にキャメルケース変換されないみたい
  • kaminari がコレクションに追加するメソッド(current_page, total_pages 等)をそのまま利用

各リソースで meta ブロックを定義

# app/resources/post_resource.rb
class PostResource < BaseResource
  root_key :data, :data
  # ... attributes, associations ...

  meta do
    if object.is_a?(Enumerable)
      paginated_meta(object)
    else
      {}
    end
  end
end
  • object は Alba が内部で保持するシリアライズ対象のオブジェクト
  • コレクション(Post.page(1).per(20) の結果)の場合のみ meta を付与
  • 単体(Post.find(id))の場合は空ハッシュ → レスポンスに meta: {} が含まれる

Controller 側

def index
  posts = Post.includes(:tags, user: { avatar_attachment: :blob }, image_attachment: :blob)
              .order(:id)
              .page(params[:page]).per(20)
  render json: PostResource.new(posts), status: :ok
end

Controller 側は特別な処理不要。kaminari でページネーションしたコレクションを渡すだけで、リソース側が自動的に meta を組み込む。

レスポンス例

{
  "data": [
    { "id": 1, "body": "...", "imageUrl": "...", "tags": [...], "user": {...} }
  ],
  "meta": {
    "currentPage": 1,
    "totalPages": 5,
    "totalCount": 100,
    "nextPage": 2,
    "prevPage": null
  }
}

※ ちなみに単体resourceの場合はこんな感じ。

{
  "data": 
    { "id": 1, "body": "...", "imageUrl": "...", "tags": [...], "user": {...} }
  ,
  "meta": {}
}

そもそもAlbaが meta keyとかをレスポンスに組み込むまでの仕組み

deepwikiを使って、いろいろ調査した。

まず render json: HogeResource.new(@hoge) とした時に何が起きているか。
@hogeは Hogeのリソースオブジェクト、HogeResourceは自分で作ったAlbaのリソース。

この時点で railsが自動的に to_jsonを呼ぶ

But you can also render JSON passing Alba::Resource object. Rails automatically calls to_json on a resource.

render json: FooResource.new(foo)

ここからRails がリソースオブジェクトに対して to_json を呼び出し、Alba は serialize → as_json → serializable_hash の流れでハッシュを構築し、で JSON 文字列に変換するらしい。

まず to_json。この子が呼ばれる。中身は何してるかは分からんが 89でserializeを読んでる。

serialize

このparamを見ても root_keyは arrayで渡せて、collectionか単体かで分けられるということがわかる。

as_json

この時点で root_keyがあるのでL103に行って
serializable_hashが呼ばれて、hash_with_metadataが呼ばれる
この時点で"h"という hashが作られる。でこの hを hash_with_metadataに渡す。

serializable_hash

ふかぼらないけど、collectionの時はそれ用のmethodが呼ばれて、returnされてる。
で話をhash_with_metadataに戻す

hash_with_metadata

でそのまま、 @_metaが呼ばれる。

こいつは以下のメソッドでセットされてる。

で最後に _metadata

blockをもらって、処理してからその結果を metaにmergeする。

つまり、今回だと、{}にmergeするので、

    {
      currentPage: collection.current_page,
      totalPages: collection.total_pages,
      totalCount: collection.total_count,
      nextPage: collection.next_page,
      prevPage: collection.prev_page
    }

こんな感じ。で、最後に hash_with_metadataの L148で
hash[key] = _metadata(block, meta)だから元のhashにくっつく。

で結果が元のresponseにmeta keyでくっついて返されるという感じらしい。

終わりに

deepwiki便利。codewikiもあったが、codewiki、UIがちょっと使いづらかった印象。
巷でAIAI祭りだが、うまく使っていきたいと思う。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?