Pagination in Gorm
Manuel Doncel Martos

Manuel Doncel Martos @manuelarte

About: Java Software Developer currently working with Go

Location:
Alphen aan den Rijn
Joined:
Sep 17, 2019

Pagination in Gorm

Publish Date: Jun 23
0 0

Gorm is the most used ORM package in Go, but, despite that, it is lacking some "basic" functionality.

One of those lacking features is Pagination.

Pagination is an essential feature for managing large datasets in web applications. It is an approach to limit and display part of the total data in a database, so then not all the table needs to be retrieved in one “go”.

While Gorm provides documentation on how to use pagination using scopes, it leaves room for improvement in flexibility and usability.

Here, I am going to explain an alternative to scopes for pagination, using Gorm’s Clauses.

Pagination Using Scopes

Gorm’s documentation introduces Scopes as a way to re use common code. In the documentation example we can see that they define a pagination scope function similar to this:

func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
  return func (db *gorm.DB) *gorm.DB {
    // validate page and pageSize
    ...
    offset := (page - 1) * pageSize
    return db.Offset(offset).Limit(pageSize)
  }
}

page := 0
pageSize := 10
db.Scopes(Paginate(page, pageSize)).Find(&users)
Enter fullscreen mode Exit fullscreen mode

So, in order to apply pagination, we need to use the code

db.Scopes(Paginate(page, pageSize))…

Pagination Using Clauses

A different and, arguably, more elegant approach is to use Gorm Clauses, this approach is a bit different to using Scopes, since Clauses are on charge of modifying the database queries, in particular, the WHERE clause.

Let’s start by defining the pagination struct

type Pagination struct {
 page          int
 pageSize      int
}

func (p *Pagination) GetPage() int {
 return p.page
}
func (p *Pagination) GetPageSize() int {
 return p.pageSize
}
Enter fullscreen mode Exit fullscreen mode

And then, let’s implement the two interfaces needed to use this struct in a gorm clause function:

clause.Expression
gorm.StatementModifier
Enter fullscreen mode Exit fullscreen mode

So the struct looks like this:

func (p *Pagination) ModifyStatement(stm *gorm.Statement) {
  // We modify statement to add the pagination
  db := stm.DB
  stm.DB.Limit(p.size).Offset((p.page - 1) * p.pageSize)
}

func (p *Pagination) Build(_ clause.Builder) {
  // The Build method is left empty since pagination does not require any additional SQL clauses.
}
Enter fullscreen mode Exit fullscreen mode

After that, you can use pagination as follow:

pagination := Pagination{
  page: 0,
  pageSize: 10,
}
db.Clauses(&pagination).Find(&users)
Enter fullscreen mode Exit fullscreen mode

To make this approach reusable and enhance its capabilities, I developed PaGorminator — a library that uses this feature for pagination, while adding other features like unpaged requests and automatic metadata population like total number of pages, and total count.

To use it, do as follow:

// Add plugin
_ = db.Use(pagorminator.PaGormMinator{})
...
pageRequest, _ := pagorminator.PageRequest(0, 10)
db.Clauses(pageRequest).Find(&users)
// this will apply pagination, and also populate pageRequest with:
// - the total number of pages
// - total count
Enter fullscreen mode Exit fullscreen mode

Comments 0 total

Загрузка комментариев...
Add comment