DEV Community

Cover image for Pagination using Gorm scopes
Rafael Firmino
Rafael Firmino

Posted on

Pagination using Gorm scopes

Gorm is a fantastic ORM writen in Go for gophers.


Today I will show you how to create pagination using Gorm Scopes.

Scopes allow you to re-use commonly used logic, the shared logic needs to be defined as type

func(*gorm.DB) *gorm.DB 
Enter fullscreen mode Exit fullscreen mode

The first step is create a Pagination struct.


//pkg.pagination    
package pkg 
 
type Pagination struct {    
    Limit        int         `json:"limit,omitempty;query:limit"`   
    Page         int         `json:"page,omitempty;query:page"` 
    Sort         string      `json:"sort,omitempty;query:sort"` 
    TotalRows    int64       `json:"total_rows"`    
    TotalPages   int         `json:"total_pages"`   
    Rows         interface{} `json:"rows"`  
}   
 
func (p *Pagination) GetOffset() int {  
    return (p.GetPage() - 1) * p.GetLimit() 
}   
 
func (p *Pagination) GetLimit() int {   
    if p.Limit == 0 {   
        p.Limit = 10    
    }   
    return p.Limit  
}   
 
func (p *Pagination) GetPage() int {    
    if p.Page == 0 {    
        p.Page = 1  
    }   
    return p.Page   
}   
 
func (p *Pagination) GetSort() string { 
    if p.Sort == "" {   
        p.Sort = "Id desc"  
    }   
    return p.Sort   
}   
Enter fullscreen mode Exit fullscreen mode


Second step is create a Gorm Scope.

func paginate(value interface{}, pagination *pkg.Pagination, db *gorm.DB) func(db *gorm.DB) *gorm.DB {  
    var totalRows int64 
    db.Model(value).Count(&totalRows)   
 
    pagination.TotalRows = totalRows    
    totalPages := int(math.Ceil(float64(totalRows) / float64(pagination.Limit)))    
    pagination.TotalPages = totalPages  
 
    return func(db *gorm.DB) *gorm.DB { 
        return db.Offset(pagination.GetOffset()).Limit(pagination.GetLimit()).Order(pagination.GetSort())   
    }   
}   
Enter fullscreen mode Exit fullscreen mode


The third step is use Gorm scope in your repository.

type CategoryGorm struct {  
    db *gorm.DB 
}   
 
func (cg *CategoryGorm) List(pagination pkg.Pagination) (*pkg.Pagination, error) {  
    var categories []*Category  
 
    cg.db.Scopes(paginate(categories,&pagination, cg.db)).Find(&categories) 
    pagination.Rows = categories    

    return &pagination, nil 
}   
Enter fullscreen mode Exit fullscreen mode

Rest Example

I won't talk about how you could implement your handles or access the repository, maybe another time.


The most important thing you need is to return the pkg.pagination for you client.


In a rest application, if I send

http://localhost:3000/categories?limit=10&page=5

Then the output would be:

{   
  "limit": 10,  
  "page": 5,    
  "sort": "Id desc",    
  "total_rows": 41, 
  "total_pages": 5, 
  "rows": [ 
    {   
      "id": "0acd5600-51b9-42e9-9fd0-ff422a6de1d1", 
      "category": "example41"   
    }   
  ] 
}   
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
stevebaros profile image
Steve Baros • Edited

Thank you this is good

for those using gin , you can dynamically pick the parameters like this

 page, _ := strconv.Atoi(context.Query("page"))
    if page <= 0 {
        page = 1
    }

    pageSize, _ := strconv.Atoi(context.Query("per_page"))
    sort := context.Query("sort")
    var sortWithDirection string
    if sort != "" {
        direction := context.Query("sortDesc")
        if direction != "" {
            if direction == "true" {
                sortWithDirection = sort + " desc"
            } else if direction == "false" {
                sortWithDirection = sort + " asc"
            }
        }

    }

    switch {
    case pageSize <= 0:
        pageSize = 10
    }
Enter fullscreen mode Exit fullscreen mode
Collapse
 
mostafabinesh profile image
Mostafa

Thanks. Was helpful

Collapse
 
gonzalezlrjesus profile image
Jesus Gonzalez

great article!

Collapse
 
sixcolors profile image
Jason McNeil • Edited

If you use this on a rest API per the post, be sure to validate the sort string against a whitelist of approved fields.

Otherwise you get SQL injection right here:

return db.Offset(pagination.GetOffset()).Limit(pagination.GetLimit()).Order(pagination.GetSort())

gorm.io/docs/security.html#SQL-inj...