Skip to content

Instantly share code, notes, and snippets.

@yogurt1
Created August 17, 2019 23:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yogurt1/772b0728c6f062aa71e20081a5fbb410 to your computer and use it in GitHub Desktop.
Save yogurt1/772b0728c6f062aa71e20081a5fbb410 to your computer and use it in GitHub Desktop.
mongoose graphql connection pagination
const { Buffer } = require('buffer');
const encodeCursor = (item, field) => {
const value = item[field];
const json = JSON.stringify(value);
const base64 = Buffer.from(json).toString('base64');
return base64;
};
const decodeCursor = cursor => {
const buffer = Buffer.from(cursor, 'base64');
const json = buffer.toString('utf8');
const data = JSON.parse(json);
return data;
};
/**
*
* @param {{ field?: string, first?: number, last?: number, after?: string, before?: string }} { first, last, after, before }
* @param {string} [field='_id'] anchor field
* @return {Promise<{
* totalCount: number,
* edges:
* {
* node: T,
* cursor: string
* }[],
* pageInfo: {
* startCursor: string,
* endCursor: string,
* hasNextPage: boolean,
* hasPreviousPage: boolean
* }
* }>} connection result
*
* @template T
*/
async function applyPagination(
{ first, last, after, before } = {},
field = '_id'
) {
/**
* @var {import('mongoose').Query}
*/
const query = this;
const totalCount = await query.count();
// cast to find
query.find();
query.limit((first || last || 0) + 1);
query.sort({
[field]: last || before ? -1 : 1,
});
if (after || before) {
const cursor = decodeCursor(after || before);
query.where(field);
if (after) {
query.gt(cursor);
} else {
query.lt(cursor);
}
}
let result = await query.exec();
const loadedMoreFirst = first && result.length > first - 1;
const loadedMoreLast = last && result.length > last - 1;
if (loadedMoreFirst) {
result = result.slice(0, result.length - 1);
} else if (loadedMoreLast) {
result = result.reverse().slice(1, result.length);
}
const edges = result.map(node => ({
node,
cursor: encodeCursor(node, field),
}));
return {
totalCount,
edges,
pageInfo: {
hasNextPage: loadedMoreFirst || !!before,
hasPreviousPage: loadedMoreLast || !!after,
startCursor: edges[0] && edges[0].cursor,
endCursor: edges[edges.length - 1] && edges[edges.length - 1].cursor,
},
};
}
function paginatePlugin(schema) {
schema.query.applyPagination = applyPagination;
}
module.exports = paginatePlugin;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment