Logo

dev-resources.site

for different kinds of informations.

Gorm Pagination With Ease

Published at
7/20/2024
Categories
go
gorm
orm
pagination
Author
sheenazien8
Categories
4 categories in total
go
open
gorm
open
orm
open
pagination
open
Author
11 person written this
sheenazien8
open
Gorm Pagination With Ease

Recently I've been struggling with pagination on Gorm, indeed gorm has an example that you need for this case, but it's a limit and offset query, the case I faced is to return the pagination information, Like Count, CurrentPage, FirstItem, HasMorePages, LastItem, LastPage, PerPage, and Total

So after a ton of browsing this case on the internet, I decided to create it.

1. Create The Struct

First, we need to define the necessary structs for pagination. We'll start by creating a resolver struct to hold all the required pagination information.

type PaginaterResolver struct {
    PaginatorInfo *PaginatorInfo `json:"paginatorInfo"`
    Data          interface{}    `json:"data"`
    model         interface{}
    request       Request
    stmt          *gorm.DB
}
Enter fullscreen mode Exit fullscreen mode

Next, we create the PaginatorInfo struct to display the pagination details.

type PaginatorInfo struct {
    Count        int  `json:"count"`
    CurrentPage  int  `json:"currentPage"`
    FirstItem    int  `json:"firstItem"`
    HasMorePages bool `json:"hasMorePages"`
    LastItem     int  `json:"lastItem"`
    LastPage     int  `json:"lastPage"`
    PerPage      int  `json:"perPage"`
    Total        int  `json:"total"`
}
Enter fullscreen mode Exit fullscreen mode

Then, we create the Request struct for pagination query parameters.

type Request struct {
    Offset   int
    Page     int
    PageSize int
}
Enter fullscreen mode Exit fullscreen mode

2. Create the Setter Methods

We'll add setter methods to set up the statement, model, and request.

func (s *PaginaterResolver) Stmt(stmt *gorm.DB) *PaginaterResolver {
    s.stmt = stmt

    return s
}

func (s *PaginaterResolver) Model(model interface{}) *PaginaterResolver {
    s.model = model

    return s
}
Enter fullscreen mode Exit fullscreen mode

For this example, we use GraphQL's ResolveParams to set the request parameters.

func (s *PaginaterResolver) Request(p graphql.ResolveParams) *PaginaterResolver {
    var page = 1
    if p.Args["page"] != nil {
        page = p.Args["page"].(int)
    }

    var pageSize = 10
    if p.Args["page_size"] != nil {
        pageSize = p.Args["page_size"].(int)
    }
    switch {
    case pageSize > 100:
        pageSize = 100
    case pageSize <= 0:
        pageSize = 10
    }
    offset := (page - 1) * pageSize
    s.request = Request{Offset: offset, Page: page, PageSize: pageSize}

    return s
}

Enter fullscreen mode Exit fullscreen mode

You can customize this to accept different request types.

type RequestParams struct {
    Page     int
    PageSize int
}
func (s *PaginaterResolver) Request(p RequestParams) *PaginaterResolver {
    // Set request based on RequestParams
    return s
}
Enter fullscreen mode Exit fullscreen mode

🎉 We've set up our initial requirements!

3. Create the Main Methods for Pagination

We need two main methods: Paginate() to fill the PaginatorInfo struct, and Paging() to apply the scope for pagination.

func (s *PaginaterResolver) Paginate() (PaginaterResolver, error) {
    var totalCount int64

    s.stmt.Model(s.model).Count(&totalCount)
    limit := s.request.PageSize
    page := s.request.Page
    offset := s.request.Offset
    lastPage := int((totalCount + int64(limit) - 1) / int64(limit))

    result := s.stmt.Scopes(s.Paging()).Find(s.model)
    if result.RowsAffected == 0 {
        config.LOG.Println("No data found")

        return PaginaterResolver{Data: []interface{}{}}, nil
    }

    paginatorInfo := &PaginatorInfo{
        Count:        int(result.RowsAffected),
        CurrentPage:  page,
        FirstItem:    offset + 1,
        HasMorePages: page < lastPage,
        LastItem:     offset + int(result.RowsAffected),
        LastPage:     lastPage,
        PerPage:      limit,
        Total:        int(totalCount),
    }

    s.PaginatorInfo = paginatorInfo
    s.Data = s.model

    return *s, nil
}
Enter fullscreen mode Exit fullscreen mode
func (s *PaginaterResolver) Paging() func(db *gorm.DB) *gorm.DB {
    return func(db *gorm.DB) *gorm.DB {
        return db.Offset(s.request.Offset).Limit(s.request.PageSize)
    }
}
Enter fullscreen mode Exit fullscreen mode

🔥 Now, we can use this pagination code in our GORM queries. Here's an example:

paginaterResolver := new(scopes.PaginaterResolver)
query := config.DB.Scopes(
    post.GetPage,
    post.GetOnlyPublish,
    scopes.Order(orderBy[0]),
).Preload(clause.Associations).Find(&posts)

if query.Error != nil {
    return scopes.PaginaterResolver{}, query.Error
}

paginater, err := paginaterResolver.
    Request(p).
    Stmt(query).
    Model(&posts).
    Paginate()
Enter fullscreen mode Exit fullscreen mode

Below is the result.

{
    "data": {
        "paymentMethods": {
            "data": [
                {
                    "id": 7,
                    "slug": "payment-method-2",
                    "type": "paymentmethod"
                },
                // More items...
            ],
            "paginatorInfo": {
                "count": 10,
                "currentPage": 1,
                "firstItem": 1,
                "hasMorePages": true,
                "lastItem": 10,
                "lastPage": 2,
                "perPage": 10,
                "total": 16
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Implementing pagination in GORM can be challenging, but by creating custom structs and methods, we can effectively manage pagination information. This approach ensures that all necessary pagination details are included in our API responses, making it easier to work with large datasets.

Feel free to customize and extend this implementation to suit your specific needs. Happy coding! 🎉

gorm Article's
26 articles in total
Favicon
Gorm: Sneak Peek of Custom Data Types
Favicon
GORM, PostgreSQL & Atlas
Favicon
Gorm Pagination With Ease
Favicon
How to Use Arrays as Parameters in Golang?
Favicon
Basic CRUD Operations Using Golang, Gin Gonic, and GORM
Favicon
GORM and Goose Migrations
Favicon
Gin + Gorm Practical Guide, Implementing a Simple Q&A Community Backend Service in One Hour
Favicon
A secret weapon to improve the efficiency of golang development, a community backend service was developed in one day
Favicon
Building CRUD Operations in Golang 🎉
Favicon
Some GORM Tips and Notes
Favicon
Gorm level UP: how to upgrade and start to use Gorm v2
Favicon
5chan API - Golang, GORM, Go-fiber
Favicon
Pagination using Gorm scopes
Favicon
GOlang URL shortener service using postgres, redis, bulma
Favicon
RestFull API Detailing
Favicon
Daily Blog
Favicon
Rest API Database Blog Post
Favicon
Prevent updating query updates all records in GORM
Favicon
Init project
Favicon
Creating a complete golang rest api from zero to Hero
Favicon
ตัวอย่างการสร้าง Many to Many Association ผ่าน GORM
Favicon
DataBinding and GORM objects
Favicon
Creating an opinionated GraphQL server with Go - Part 4
Favicon
Creating an opinionated GraphQL server with Go - Part 3
Favicon
Grails: Mocking domain objects/criteria that reference Joda Time objects
Favicon
Go's ORM framework GORM best practices indefinite parameter usage

Featured ones: