Skip to content

Instantly share code, notes, and snippets.

@darthtrevino
Last active February 6, 2018 17:52
Show Gist options
  • Save darthtrevino/c1a3ec29e8502dea5291 to your computer and use it in GitHub Desktop.
Save darthtrevino/c1a3ec29e8502dea5291 to your computer and use it in GitHub Desktop.
A Helper for Implementing Relay's Cursor-Based Pagination with Sequelize
const { fromGlobalId, toGlobalId } = require("graphql-relay");
import {FindOptions} from "sequelize";
export interface ICursorPageable {
// Backward Paging Arguments
before?: string;
last?: number;
// Forward Paging Arguments
after?: string;
first?: number;
}
/**
* This method will mutate the incoming criteria object with paging arguments that align to the
* Relay's Paging interface (last/before, first/after). The query will be paged against the query's results,
* not physical identifiers.
*/
export function addCursorPagingCriteria(criteria: FindOptions, args: ICursorPageable) {
const {first, last, after, before} = args;
const isForwardPaging = !!first;
const isBackwardPaging = !!last;
const getGlobalId = cursor => parseInt(fromGlobalId(cursor).id, 10);
if (isForwardPaging && isBackwardPaging) {
throw new Error("cursor-based pagination cannot be forwards AND backwards");
}
if (isForwardPaging && before || isBackwardPaging && after) {
throw new Error("paging must use either first/after or last/before");
}
if (isForwardPaging && first < 0 || isBackwardPaging && last < 0) {
throw new Error("paging limit must be positive");
}
if (isForwardPaging) {
criteria.limit = first;
if (after) {
criteria.offset = getGlobalId(after) + 1 || 0;
}
} else if (isBackwardPaging) {
criteria.limit = last;
if (before) {
let offset = getGlobalId(before) - last;
// Check if this query underflows.
if (offset < 0) {
// Adjust the limit with the underflow value
criteria.limit = Math.max(last + offset, 0);
offset = 0;
}
criteria.offset = offset;
}
}
}
@darthtrevino
Copy link
Author

usage:

resolve(root, args) {
    const criteria = makeCriteria(args);
    addCursorPagingCriteria(criteria, args);                               
    return connectionFromPromisedArraySlice(
        repository.findAll(criteria),  // the repository adds a default order property to the criteria if none exists
        args, 
        {sliceStart: criteria.offset, arrayLength: Number.MAX_VALUE}
    );
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment