In Carely of our service, the server side is implemented by Ruby (on Rails), and the exchange of data with the front (Vue) is implemented by graphQL. Pagination in Rails often uses a gem called Kaminari, but in the case of graphQL, Relay-Style Cursor Pagination seems to be standard, so I tried both implementation methods.
kaminari is version 1.2.1 graphql-ruby is version 1.10.10 is.
(URL of graphql-ruby Pagination description) https://graphql-ruby.org/pagination/using_connections.html
Describe to use Pagination Plugin in Schema Class as shown below.
class MySchema < GraphQL::Schema
.
.
use GraphQL::Pagination::Connections
.
end
Use the description :: connection_type
to define the Query to which you want to add Pagination functionality.
field :users, Types::UserType::connection_type, null: true do
.
.
argument :name, String, "name", required: false
.
end
That's all for the server-side implementation.
You will be able to specify parameters for first (last), after (before)
.
With the query below, 10 items will be acquired from first and 10 items will be acquired from after by specifying after.
For the character string specified in after, specify the character string obtained by cursor.
Also, a field called pageInfo
can be specified, and is there a previous page or a next page? You can get the position of the start and end cursors.
query MyQuery {
users (first: 10, after: "xxxx") {
pageInfo {
hasPreviousPage
hasNextPage
endCursor
startCursor
}
edges {
cursor
node {
firstName
lastName
mailAddress
age
.
.
}
}
##Can be taken with nodes
nodes {
firstName
lastName
mailAddress
age
.
.
}
##Example result
{
"data": {
"users": {
"pageInfo": {
"hasPreviousPage": false,
"hasNextPage": true,
"endCursor": "MTA",
"startCursor": "MQ"
},
"edges": [
{
"cursor": "MQ",
"node": {
"firstName": "Hogehoge",
"lastName": "Fuga Fuga",
"mailAddress": "[email protected]",
"age": "20"
}
},
{
"cursor": "Mg",
"node": {
"firstName": "Hogehoge 2",
"lastName": "Fuga Fuga 2",
"mailAddress": "[email protected]",
"age": "30"
}
},
.
.
],
"nodes": [
{
"firstName": "Hogehoge",
"lastName": "Fuga Fuga",
"mailAddress": "[email protected]",
"age": "20"
},
{
"firstName": "Hogehoge 2",
"lastName": "Fuga Fuga 2",
"mailAddress": "[email protected]",
"age": "30"
},
.
.
]
The general methods used for Pagination in kaminari are as follows.
#Get the first page divided into 10 cases
User.page(1).per(10)
#total number
User.page(1).per(10).total_count
#number of tolal pages
User.page(1).total_pages
#Number of pages per page
User.page(1).limit_value
#Current number of pages
User.page(1).current_page
#Number of next pages
User.page(1).next_page
#Number of previous pages
User.page(1).prev_page
#Whether it is the first page
User.page(1).first_page?
#Whether it is the last page
User.page(1).last_page?
Create a Type for Pagination as shown below.
module Types
class PaginationType < Types::BaseObject
field :total_count, Int, null: true
field :limit_value, Int, null: true
field :total_pages, Int, null: true
field :current_page, Int, null: true
end
end
Create ʻUserType and ʻUsersType
that returns multiple User information and Pagination as follows.
module Types
class UserType < Types::BaseObject
field :uuid, String, null: true
field :first_name, String, null: true
field :last_name, String, null: true
field :mail_address, String, null: true
field :age, String, null: true
.
.
end
end
module Types
class UsersType < Types::BaseObject
field :pagination, PaginationType, null: true
field :users, [UserType], null: true
end
end
Add the following processing to Query to return pagination information.
#Page with argument,Added to be able to pass per
field :users, Types::UserType, null: true do
.
.
argument :name, String, "name", required: false
argument :page, Int, required: false
argument :per, Int, required: false
.
end
#Use kaminari pagination if there are arguments page and per
def users(**args)
.
.
users = User.page(args[:page]).per(args[:per])
{
users: users,
pagination: pagination(users)
}
end
#Return count using kaminari method
def pagination(result)
{
total_count: result.total_count,
limit_value: result.limit_value,
total_pages: result.total_pages,
current_page: result.current_page
}
end
This is an example of a query that retrieves the first page divided into 10 items and the result.
query MyQuery {
users (per:10, page:1) {
pagination {
currentPage
limitValue
totalCount
totalPages
}
users {
firstName
lastName
mailAddress
age
.
.
}
}
##Example result
{
"data": {
"users": {
"pagination": {
"currentPage": 1,
"limitValue": 10,
"totalCount": 100,
"totalPages": 10
},
"users": [
{
"firstName": "Hogehoge",
"lastName": "Fuga Fuga",
"mailAddress": "[email protected]",
"age": "20"
},
{
"firstName": "Hogehoge 2",
"lastName": "Fuga Fuga 2",
"mailAddress": "[email protected]",
"age": "30"
},
.
.
]
Relay-Style Cursor Pagination
It looks good because it is easy to use if you just search for information with API.
Since it only has the location information by cursor, if you want to create a UI that displays the total number of cases and the total number of pages on the front side, Custom ) Seems to need to create a connection
.
kaminari
If you use only in-house engineers and create a UI that displays the total number of cases and the total number of pages on the front side, using kaminari is less man-hours.
Personally, I think it would be better to create a custom connection using Relay-Style Cursor Pagination
because it suits the style of graphQL.
Recommended Posts