0
0
GraphqlHow-ToBeginner · 4 min read

How to Implement Pagination in GraphQL: Syntax and Example

To implement pagination in GraphQL, use arguments like first and after for cursor-based pagination or limit and offset for offset-based pagination. Cursor-based pagination is preferred for efficient and reliable navigation through large datasets.
📐

Syntax

Pagination in GraphQL typically uses either cursor-based or offset-based methods. Cursor-based pagination uses first to specify how many items to fetch and after to indicate the cursor after which to start. Offset-based pagination uses limit and offset to control the number of items and starting position.

Cursor-based pagination is more reliable for dynamic data because it uses opaque cursors instead of numeric offsets.

graphql
type Query {
  items(first: Int, after: String): ItemConnection
}

type ItemConnection {
  edges: [ItemEdge]
  pageInfo: PageInfo
}

type ItemEdge {
  cursor: String
  node: Item
}

type PageInfo {
  hasNextPage: Boolean
  endCursor: String
}
💻

Example

This example shows a GraphQL query using cursor-based pagination to fetch the first 3 items after a given cursor. The server returns edges with nodes and cursors, plus pageInfo to know if more pages exist.

graphql
query GetItems($first: Int!, $after: String) {
  items(first: $first, after: $after) {
    edges {
      cursor
      node {
        id
        name
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

# Variables:
# {
#   "first": 3,
#   "after": "cursor123"
# }
Output
{ "data": { "items": { "edges": [ {"cursor": "cursor124", "node": {"id": "4", "name": "Item 4"}}, {"cursor": "cursor125", "node": {"id": "5", "name": "Item 5"}}, {"cursor": "cursor126", "node": {"id": "6", "name": "Item 6"}} ], "pageInfo": { "hasNextPage": true, "endCursor": "cursor126" } } } }
⚠️

Common Pitfalls

  • Using offset-based pagination on rapidly changing data can cause duplicates or missing items.
  • Not returning pageInfo with hasNextPage and endCursor makes it hard to know if more data exists.
  • Exposing raw database IDs as cursors can leak internal details; use encoded opaque cursors instead.
graphql
query WrongPagination {
  items(limit: 3, offset: 0) {
    id
    name
  }
}

# Right way uses cursor-based pagination:
query RightPagination($first: Int!, $after: String) {
  items(first: $first, after: $after) {
    edges {
      cursor
      node {
        id
        name
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
📊

Quick Reference

ConceptDescriptionExample Argument
Cursor-based PaginationUse cursors to mark position, safer for dynamic datafirst, after
Offset-based PaginationUse numeric offset and limit, simpler but less reliablelimit, offset
EdgesList of items with cursorsedges { cursor, node }
PageInfoInfo about next page availability and cursorpageInfo { hasNextPage, endCursor }

Key Takeaways

Use cursor-based pagination with first and after for reliable navigation.
Always return pageInfo with hasNextPage and endCursor to help clients paginate.
Avoid offset-based pagination on frequently changing data to prevent inconsistent results.
Encode cursors to keep them opaque and avoid exposing internal IDs.
Structure your schema with edges and pageInfo for standard pagination support.