Skip to main content

How Pagination Works

The Legalesign GraphQL API uses cursor-based pagination following the Relay connection specification. Every list of items — documents, templates, contacts, invoices — uses this same pattern.

The Connection Pattern

The terminology comes from graph theory — nodes are points, edges are lines connecting them. In the API, an edge describes the relationship between two types and can carry additional metadata (like which permissions a User has in a Group).

When you query a list, you don't get a plain array. You get a connection object with three parts:

documentConnection(first: 20) {
documents { ... } # the items (shortcut)
totalCount # total matching items
pageInfo { # pagination cursors
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}

Why Not Just an Array?

Cursor-based pagination is stable. Unlike offset pagination (page=2), cursors don't break when items are added or removed between requests. This matters when documents are being sent and signed in real time.

Fetching the First Page

Use first to limit results. When paginating through a full result set in created order, pass "0" or "START" as the after argument on your first call to signal that you're starting a paginated sequence:

query {
group(id: "grpYourGroupId") {
documentConnection(first: 100, after: "0") {
documents {
id
name
status
created
}
pageInfo {
endCursor
hasNextPage
}
totalCount
}
}
}

Fetching the Next Page

If hasNextPage is true, pass endCursor as the after argument:

query {
group(id: "grpYourGroupId") {
documentConnection(first: 100, after: "eyJpZCI6ImRvYzEyMyJ9") {
documents {
id
name
status
created
}
pageInfo {
endCursor
hasNextPage
}
totalCount
}
}
}

Repeat until hasNextPage is false.

Pagination Arguments

All connections accept these arguments:

ArgumentTypeDescription
firstIntNumber of items to return from the start
afterIDCursor to start after (for forward pagination)
lastIntNumber of items to return from the end
beforeIDCursor to start before (for backward pagination)

Most connections also accept filter arguments specific to the data type. For example, documentConnection accepts from, to, status, search, sendType, and sender.

The Edges Shortcut

Connections provide both edges (with cursor per item) and a shortcut list. For most use cases the shortcut is simpler:

# Shortcut — simpler, no per-item cursor
documentConnection(first: 20) {
documents {
id
name
}
pageInfo {
endCursor
hasNextPage
}
}

# Full edges — needed if you want per-item cursors
documentConnection(first: 20) {
edges {
node {
id
name
}
cursor
}
pageInfo {
endCursor
hasNextPage
}
}

Use the shortcut unless you need individual cursors (e.g. for deleting a specific item and resuming from that point).

note

totalCount returns the count for the current page, not the total across all pages. The cost of computing a global total across potentially millions of records makes a true total impractical.

Real-World Example

Here's how the Legalesign Console app paginates documents using infinite scroll with TanStack Query:

query queryInfiniteDocuments {
group(id: "grpYourGroupId") {
documentConnection(
first: 100
after: "eyJpZCI6ImRvYzEyMyJ9"
sendType: SINGLE
from: "2025-01-01T00:00:00Z"
to: "2025-06-01T00:00:00Z"
) {
documents {
id
name
status
senderName
created
recipients {
id
email
status
signedDateTime
}
}
pageInfo {
endCursor
hasNextPage
}
totalCount
}
}
}

The app fetches the first page, then on scroll uses endCursor as after to load the next batch.

Which Types Use Connections?

Every list relationship in the API uses this pattern:

  • Group.documentConnection, templateConnection, batchConnection, contactConnection, experienceConnection, scheduleConnection, attachmentConnection, memberConnection, invitationConnection, standardMessageConnection, contactGroupConnection, draftConnection, activityConnection
  • Organisation.groupConnection, dataStopConnection, dataDeletionConnection, retentionConnection, userConnection, invoiceConnection, dataSubjectConnection
  • Batch.documentConnection
  • Billing.invoiceConnection
  • Invoice.lineItemConnection
  • Template.elementConnection, userSignatureConnection
  • Document.elementConnection
  • Recipient.elementConnection
  • User.memberConnection, organisationConnection, supportTicketConnection