https://github.com/rosberry/go-pagination
Simple library to convert cursor to query and back
https://github.com/rosberry/go-pagination
golang library
Last synced: over 1 year ago
JSON representation
Simple library to convert cursor to query and back
- Host: GitHub
- URL: https://github.com/rosberry/go-pagination
- Owner: rosberry
- License: mit
- Created: 2020-11-20T07:06:16.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2022-02-21T12:26:25.000Z (over 4 years ago)
- Last Synced: 2025-01-31T13:43:02.023Z (over 1 year ago)
- Topics: golang, library
- Language: Go
- Homepage:
- Size: 289 KB
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Pagination library
Package go-pagination was designed to paginate simple RESTful APIs with [GORM](https://github.com/go-gorm/gorm) and [Gin](https://github.com/gin-gonic/gin).
It uses cursor-based strategy, that avoids many of the pitfalls of "offset–limit" pagination.
## Usage
#### Model
```go
package models
import "github.com/rosberry/go-pagination"
type User struct {
ID uint
Name string
Role uint `json:"roleID" cursor:"roleID"`
}
func GetUsersList(role uint, paginator *pagination.Paginator) []User {
var users []User
q := db.DB.Model(&User{}).Where("role = ?", role)
err := paginator.Find(q, &users)
if err != nil {
log.Println(err)
return nil
}
return users
}
```
#### Controllers
```go
package controllers
import "github.com/rosberry/go-pagination"
type (
usersListResponse struct {
Result bool
Users []userData
Pagination *pagination.PageInfo
}
)
func UsersList(c *gin.Context) {
paginator, err := pagination.New(pagination.Options{
GinContext: c,
DB: models.GetDB(),
Model: &models.User{},
Limit: 5,
DefaultCursor: nil,
})
if err != nil {
log.Println(err)
}
users := models.GetUsersList(0, paginator)
c.JSON(200, usersListResponse{
Result: true,
Users: usersListToData(users),
Pagination: paginator.PageInfo,
})
}
```
### Customize request
If you want to get values in a special way, you can customize the functions to find the values you need.
You must implement functions `RequestGetter` type
```go
type RequestGetter func(c *gin.Context) (query string)
```
for example
```go
func cursorGetter(c *gin.Context) (query string) {
cursorQuery := c.Request.Header.Get("customCursorFromHeader")
return cursorQuery
}
func sortingGetter(c *gin.Context) (query string) {
sortingQuery := c.Query("sort")
return sortingQuery
}
```
and pass the functions as `Options.CustomRequest` (type `RequestOptions`) in `pagination.New()` function.
```go
paginator, err := New(Options{
GinContext: c,
Limit: uint(limit),
DB: db,
Model: &Material{},
CustomRequest: &RequestOptions{
Cursor: func(c *gin.Context) (query string) {
cursorQuery := c.Request.Header.Get("customCursorFromHeader")
return cursorQuery
},
Sorting: func(c *gin.Context) (query string) {
sortingQuery := c.Query("sort")
return sortingQuery
},
},
})
```
- `query` for `cursor`/`after`/`before` - base64 string
- `query` for `sorting` - json string
## Client-Server interaction
Request:
```
GET /items
```
```
GET /items?sorting=%5B%7B%22field%22:%22id%22,%22direction%22:%22desc%22%7D%5D
```
Sorting query parameters is JSON:
```
[
{
"field": "Name",
"direction": "asc"
},
{
"field": "UpdatedAt",
"direction": "desc"
}
]
```
field name in query parameters should have name from response. If name in response is different from name in model - you need add tag cursor:"fieldName" in model
Response:
```
{
"result": true,
"data": {
},
"pagination": {
"hasPrev": false,
"hasNext": true,
"prev": "ew2YWxU0Cn01ZSI6ogICJID=",
"next": "ewogICJ2YWx1ZSI6IDU0Cn0=",
"totalRows": 10,
"rangeTruncated": true
}
}
```
Request next page:
```
GET /items?cursor=ew2YWxU0Cn01ZSI6ogICJID
```
Response (end):
```
{
"result": true,
"data": null,
"pagination:" null
}
```
### Before / After
You can use `after`/`before` params instead of `cursor` in request
```
GET /items?after=e2sdw2wO0WDwwW&before=sdqqwDsdDq2Pd1
```
The `after` parameter is typically sent by the client to get the next page, while `before` is used to get the prior page.
Clients MAY use the `after` and `before` parameters together on the same request. These are called “range pagination requests”, as the client is asking for all the results starting from immediately after the `after` cursor and continuing up until the `before` cursor.
For range pagination requests, the server uses a `limit` to determine the maximum page size. In other words, the page size used will depend on the value of the `limit` parameter or the maximum page size.
If the number of results that satisfy both the `after` and `before` constraints exceeds the used page size, the server responds with the same paginated data that it would have if the `before` parameter had not been provided. However, in this case the server MUST also add `"rangeTruncated": true` to the pagination metadata to indicate to the client that the paginated data does not contain all the results it requested.
### Field naming

## About

This project is owned and maintained by [Rosberry](http://rosberry.com). We build mobile apps for users worldwide 🌏.
Check out our [open source projects](https://github.com/rosberry), read [our blog](https://medium.com/@Rosberry) or give us a high-five on 🐦 [@rosberryapps](http://twitter.com/RosberryApps).
## License
This project is available under the MIT license. See the LICENSE file for more info.