Problem
How do we figure out if there is a next page during pagination?
Solution
When querying the database, artificially grab one more item than the client asked for.
This lets us peek into the future. 👀
Then we can ask, “Is the length of the retrieved list longer than the number of items the client actually asked for?”
If yes, it means there’s at least one more item waiting for them on the next page.
If no, it means they’ve reached the end of the list.
We can ask this question like this:
const hasNextPage = posts.length > limit;
posts
is the result of a database request where we’ve artificially set the limit to one more than the client asked for.
limit
is the actual limit that the client asked for.
We then trim off the extra node before returning the data to the client.
Implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
Query: { | |
posts: async (parent, { cursor, limit = 100 }, { models }) => { | |
const whereOptions = cursor | |
? { | |
where: { | |
createdAt: { [Op.lt]: cursor } | |
} | |
} | |
: {}; | |
// gives us an array of posts | |
const posts = await models.Post.findAll({ | |
...whereOptions, | |
limit: limit + 1, // grab 1 more than the provided limit | |
order: [["createdAt", "DESC"]] | |
}); | |
// If the list of returned messages is longer than the limit, | |
// there must be a next page. | |
// (since we added this artificially to check if hasNextPage) | |
const hasNextPage = posts.length > limit; | |
// If there is a next page, return the whole array except for the last item | |
// (since we added this artificially to check if hasNextPage) | |
// If there is no next page, return the whole posts array. | |
const nodes = hasNextPage ? posts.slice(0, -1) : posts; | |
// put the data in the shape our client expects | |
// (each edge contains a node) | |
const edges = nodes.map((node) => { | |
return { node: node }; | |
}); | |
// return edges + page info | |
return { | |
edges: edges, | |
pageInfo: { | |
hasNextPage: hasNextPage, | |
endCursor: nodes[nodes.length - 1].createdAt | |
} | |
}; | |
}, | |
} | |
} |