GraphQL
Pagination
List fields are Relay connections. Use first/after or last/before, read pageInfo, and do not treat a single page as the full set.
Every has_many relation in the GraphQL schema is a Relay connection. The list field accepts first, after, last, and before. The result has nodes, edges, pageInfo, and totalCount.
query Rfis($organizationId: ID!, $projectId: ID!, $after: String) {
organization(id: $organizationId) {
project(id: $projectId) {
rfis(first: 50, after: $after) {
totalCount
nodes {
id
number
subject
status
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
PageInfo
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
Walk forward with first + after: pageInfo.endCursor while hasNextPage is true. Walk backward with last + before: pageInfo.startCursor while hasPreviousPage is true.
totalCount is the full filtered set, not the page size. Use it to know when you still have pages left, but still follow pageInfo rather than counting nodes yourself.
Edges
edges is the Relay edge list (cursor + node). Prefer nodes unless you need per-item cursors.
Search
Project search is also a connection:
query Search($organizationId: ID!, $projectId: ID!, $query: String!) {
organization(id: $organizationId) {
project(id: $projectId) {
search(query: $query, first: 25) {
totalCount
nodes {
id
type
typeDisplayName
title
path
sanitizedHeadline
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
search is indexed project content (drawings, documents, submittals, specs, and other searchable records). It is not a substitute for listing a single type with rfis / submittals / and so on.
Completeness
Do not report a total or an "all records" answer from the first page. Loop until hasNextPage is false, or stop after a documented cap and say the result is partial.