Pagination
The Picked API paginates every list field that can return a large result. It uses a cursor, in the Relay-style pattern. This guide covers the connection pattern and shows how to move through a result.
Connection pattern
A paginated field returns a connection object with this shape:
type ExampleConnection {
edges: [ExampleEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type ExampleEdge {
cursor: String!
node: Example!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}| Field | Description |
|---|---|
edges | An ordered list of edges. Each edge holds a cursor and a node, and the node holds the data. |
pageInfo.hasNextPage | true when more results come after the last edge |
pageInfo.hasPreviousPage | true when results come before the first edge |
pageInfo.startCursor | The cursor of the first edge on the current page |
pageInfo.endCursor | The cursor of the last edge on the current page |
totalCount | The number of items on every page together |
Forward pagination with first and after
To move forward through the results, use two arguments. The first argument gives
the page size. The after argument gives the cursor to start after.
First page
Leave the after argument out to read the first page:
query {
organizations(input: { first: 10 }) {
edges {
cursor
node {
id
name
}
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}Next page
Pass the endCursor from the previous response as the after argument:
query {
organizations(input: { first: 10, after: "cursor_from_previous_page" }) {
edges {
cursor
node {
id
name
}
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}Check for more pages
Read the hasNextPage field before you ask for the next page:
hasNextPage: true— more results are available. PassendCursoras theafterargument of the next request.hasNextPage: false— you read every result. Stop here.
Full paginated query example
This example reads every member of an organization, one page at a time:
# Page 1
query {
organization(input: { slug: "my-company" }) {
members(first: 20) {
edges {
cursor
node {
id
userEmail
role
joinedAt
}
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
}If pageInfo.hasNextPage is true, fetch the next page:
# Page 2
query {
organization(input: { slug: "my-company" }) {
members(first: 20, after: "cursor_from_page_1") {
edges {
cursor
node {
id
userEmail
role
joinedAt
}
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
}Continue until hasNextPage returns false.
Paginated fields
These fields in the schema return a connection type. They all take the first and
after arguments:
| Parent Type | Field | Connection Type |
|---|---|---|
Query | organizations | OrganizationConnection |
User | organizations | OrganizationConnection |
User | invitations | ViewerInvitationConnection |
Organization | members | OrganizationMemberConnection |
Organization | invitations | OrganizationInvitationConnection |
Organization | projects | ProjectConnection |
Project | reports | ProjectReportConnection |
All of these fields accept the same first and after arguments and return the standard edges, pageInfo, and totalCount structure.
Tips
- The default page size — the API applies a default limit when you leave
firstout. Setfirstyourself to make sure that each page has the size you expect. - A cursor is opaque — treat a cursor string as an opaque token. Do not parse it, build it, or change it. Pass it back exactly as the API gave it.
- The order is stable — a connection returns its results in a stable order. The same cursor always points at the same position in the result.
- Use them with other fields — you can use the pagination arguments together with other query fields. One request that reads an organization and the first page of its members is common, and it is efficient.
