Gqlgen GraphQL Resolver Cursor Pagination
Implement Relay-style cursor pagination with gqlgen resolvers, resolving edges and pageInfo from a keyset-ordered store.
Code
package main
// gqlgen resolver implementing Relay-style GraphQL cursor pagination
import (
"context"
"fmt"
"time"
)
type Order struct {
ID string `json:"id"`
CreatedAt time.Time `json:"createdAt"`
Total int `json:"total"`
}
type OrderEdge struct {
Cursor string `json:"cursor"`
Node Order `json:"node"`
}
type PageInfo struct {
HasNextPage bool `json:"hasNextPage"`
EndCursor string `json:"endCursor"`
}
type OrderConnection struct {
Edges []OrderEdge `json:"edges"`
PageInfo PageInfo `json:"pageInfo"`
}
type Resolver struct {
orders []Order
}
func (r *Resolver) Orders(
ctx context.Context, first *int, after *string,
) (*OrderConnection, error) {
limit := 20
if first != nil && *first > 0 && *first <= 100 {
limit = *first
}
// 存储按时间倒序,游标即 createdAt 的 ISO 串
afterTime := time.Time{}
if after != nil {
parsed, err := time.Parse(time.RFC3339, *after)
if err != nil {
return nil, fmt.Errorf("invalid cursor: %w", err)
}
afterTime = parsed
}
edges := make([]OrderEdge, 0, limit)
for _, o := range r.orders {
if !afterTime.IsZero() && !o.CreatedAt.After(afterTime) {
continue // 跳过游标之前(含锚点)的记录
}
if len(edges) == limit {
break
}
edges = append(edges, OrderEdge{
Cursor: o.CreatedAt.Format(time.RFC3339),
Node: o,
})
}
hasNext := false
lastCursor := ""
if len(edges) > 0 {
last := edges[len(edges)-1].Node.CreatedAt
lastCursor = edges[len(edges)-1].Cursor
for _, o := range r.orders {
if o.CreatedAt.After(last) {
hasNext = true
break
}
}
}
return &OrderConnection{
Edges: edges,
PageInfo: PageInfo{HasNextPage: hasNext, EndCursor: lastCursor},
}, nil
}Report a problem with this exampleOpens a pre-filled issue on GitHub.