Golang / GoLang Production Patterns and Web Standards Interview Questions
How do you implement pagination for list endpoints in a Go REST API?
Two common pagination strategies: offset-based (page/limit) and cursor-based (token-based). Cursor pagination scales better for large datasets and handles real-time data insertion without the duplicate/skip problems of offset pagination.
// Offset-based pagination (simple, common for small datasets) type OffsetPagination struct { Page int `json:"page"` // 1-indexed PerPage int `json:"per_page"` Total int `json:"total"` } func listUsersHandler(w http.ResponseWriter, r *http.Request) error { page := parseIntParam(r, "page", 1) perPage := parseIntParam(r, "per_page", 20) if perPage > 100 { perPage = 100 } // cap per-page offset := (page - 1) * perPage users, total, err := userRepo.List(r.Context(), offset, perPage) if err != nil { return err } return writeJSON(w, http.StatusOK, map[string]any{ "data": users, "pagination": OffsetPagination{Page: page, PerPage: perPage, Total: total}, }) } // Cursor-based pagination (better for large/live datasets) type CursorPage struct { Data any `json:"data"` NextCursor string `json:"next_cursor,omitempty"` HasMore bool `json:"has_more"` } func listUsersCursor(w http.ResponseWriter, r *http.Request) error { cursor := r.URL.Query().Get("cursor") limit := parseIntParam(r, "limit", 20) // Decode opaque cursor (e.g., base64(id+timestamp)) var afterID int if cursor != "" { afterID = decodeCursor(cursor) } users, err := userRepo.ListAfterID(r.Context(), afterID, limit+1) if err != nil { return err } hasMore := len(users) > limit if hasMore { users = users[:limit] } var nextCursor string if hasMore { nextCursor = encodeCursor(users[len(users)-1].ID) } return writeJSON(w, http.StatusOK, CursorPage{ Data: users, NextCursor: nextCursor, HasMore: hasMore, }) }
More Related questions...