Skip to content

Instantly share code, notes, and snippets.

@xkisu
Created June 4, 2021 02:47
Show Gist options
  • Save xkisu/ad6391368a90c94e09bd5e06e49369ae to your computer and use it in GitHub Desktop.
Save xkisu/ad6391368a90c94e09bd5e06e49369ae to your computer and use it in GitHub Desktop.
Golang Postgres Query Builder for GraphQL Relay-style Cursor-Based Pagination
package filters
import (
"fmt"
sq "github.com/Masterminds/squirrel"
)
// Pagination provides parameters for a pagination filter.
type Pagination struct {
// First specifies a count of the first items to retrieve.
First int `json:"first"`
// Last
Last int `json:"last"`
// Before specifies a cursor to select items from before.
Before string `json:"before"`
// After specifies a cursor to select items after.
After string `json:"after"`
// NOTES:
// When before: cursor is used, the edge closest to cursor must come last in the result edges.
// @see https://relay.dev/graphql/connections.htm#sec-Edge-order
//
// When after: cursor is used, the edge closest to cursor must come first in the result edges.
// @see https://relay.dev/graphql/connections.htm#sec-Edge-order
}
// Apply pagination clauses to the specified query builder.
func (p Pagination) Apply (builder sq.SelectBuilder) (sq.SelectBuilder, error) {
if p.Before != "" && p.After != "" {
return sq.SelectBuilder{}, fmt.Errorf("pagniation before and after cannot both be specified")
}
if p.Before != "" {
builder = builder.Where(sq.Lt{
"id": p.Before,
})
} else if p.After != "" {
builder = builder.Where(sq.Gt{
"id": p.After,
})
}
if p.First > 0 || p.Last > 0 {
if p.First > 0 {
builder = builder.OrderBy("id DESC", "created_at DESC").
Limit(uint64(p.First) + 1)
} else if p.Last > 0 {
builder = builder.OrderBy("id ASC", "created_at ASC").
Limit(uint64(p.Last) + 1)
} else {
builder = builder.OrderBy("id DESC", "created_at DESC").
Limit(25)
}
}
return builder, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment