graphql-rubyでページネーションを実装する

November 26, 2022

確認環境

$ bundle exec ruby --version
ruby 2.7.5p203 (2021-11-24 revision f69aeb8314) [x86_64-darwin19]
$ bundle exec rails --version
Rails 6.0.4.6
$ bundle info graphql
  * graphql (2.0.13)
	Summary: A GraphQL language and runtime for Ruby
	Homepage: https://github.com/rmosolgo/graphql-ruby
	Path: /Users/xxxxx/.rbenv/versions/2.7.5/lib/ruby/gems/2.7.0/gems/graphql-2.0.13

データ準備

テーブル定義確認

$ rails db
sqlite> .schema posts
CREATE TABLE IF NOT EXISTS "posts" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar, "description" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);

データを投入

$ rails c
Loading development environment (Rails 6.0.4.6)
irb(main):001:1* 10.times do |i|
irb(main):002:1*   Post.create!(title: "title_#{i}", description: "desc_#{i}")
irb(main):003:0> end

検証コードを実装

app/graphql/types/post_type.rb

# frozen_string_literal: true

module Types
  class PostType < Types::BaseObject
    field :id, ID, null: false
    field :title, String
    field :description, String
    field :created_at, GraphQL::Types::ISO8601DateTime, null: false
    field :updated_at, GraphQL::Types::ISO8601DateTime, null: false
  end
end

app/graphql/types/query_type.rb

module Types
  class QueryType < Types::BaseObject
    # Add `node(id: ID!) and `nodes(ids: [ID!]!)`
    include GraphQL::Types::Relay::HasNodeField
    include GraphQL::Types::Relay::HasNodesField

    field :paging_post, Types::PostType.connection_type, null: true
    def paging_post
      Post.all.order(id: :desc)
    end

    ...

graphiql でクエリを発行する

1ページ目

query {
  pagingPost(first: 2) {
    edges {
      node {
        id
        title
      }
      cursor
    }
    nodes {
      id
      title
    }
    pageInfo {
      startCursor
      endCursor
      hasPreviousPage
      hasNextPage
    }
  }
}

取得結果

{
  "data": {
    "pagingPost": {
      "edges": [
        {
          "node": {
            "id": "11",
            "title": "title_9"
          },
          "cursor": "MQ"
        },
        {
          "node": {
            "id": "10",
            "title": "title_8"
          },
          "cursor": "Mg"
        }
      ],
      "nodes": [
        {
          "id": "11",
          "title": "title_9"
        },
        {
          "id": "10",
          "title": "title_8"
        }
      ],
      "pageInfo": {
        "startCursor": "MQ",
        "endCursor": "Mg",
        "hasPreviousPage": false,
        "hasNextPage": true
      }
    }
  }
}

2ページ目

1ページ目の結果で endCursor に “Mg” のあとの結果を取得するように指定します。

query {
  pagingPost(first: 2, after: "Mg") {
    edges {
      node {
        id
        title
      }
      cursor
    }
    nodes {
      id
      title
    }
    pageInfo {
      startCursor
      endCursor
      hasPreviousPage
      hasNextPage
    }
  }
}

取得結果

{
  "data": {
    "pagingPost": {
      "edges": [
        {
          "node": {
            "id": "9",
            "title": "title_7"
          },
          "cursor": "Mw"
        },
        {
          "node": {
            "id": "8",
            "title": "title_6"
          },
          "cursor": "NA"
        }
      ],
      "nodes": [
        {
          "id": "9",
          "title": "title_7"
        },
        {
          "id": "8",
          "title": "title_6"
        }
      ],
      "pageInfo": {
        "startCursor": "Mw",
        "endCursor": "NA",
        "hasPreviousPage": true,
        "hasNextPage": true
      }
    }
  }
}

参考


SHARE

Profile picture

Written by tamesuu