uwindEdges
is a simple utility for extracting nodes from GraphQL responses that are paginated with the Relay Cursor Connection spec. This structure is handy for handling pagination in your requests, but after that, you usually just want the nodes.
You can also use the paginate
export to transform an array of nodes into a paginated object.
Takes a Cursor Connection and returns a tuple of (1) simple array of the nodes, with an additional __cursor
property, and (2) the original page info.
Example:
import { unwindEdges } from '@good-idea/unwind-edges'
const response = {
allUsers: {
pageInfo: {
hasNextPage: true,
hasPreviousPage: false,
},
edges: [
{
cursor: 'x123',
node: {
id: 'abc',
name: 'frank',
},
},
{
cursor: 'y234',
node: {
id: 'def',
name: 'ursula',
},
},
{
cursor: 'z456',
node: {
id: 'ghi',
name: 'ira',
},
},
],
},
}
const [users, { pageInfo, firstCursor, lastCursor }] = unwindEdges(response.allUsers)
console.log(users)
/**
* [
* { id: 'abc', name: 'frank', __cursor: 'x123' },
* { id: 'def', name: 'ursula', __cursor: 'y234' },
* { id: 'ghi', name: 'ira', __cursor: 'z456' }
* ]
*/
console.log(pageInfo)
/**
* {
* hasPreviousPage: false,
* hasNextPage: true
* }
*/
console.log(firstCursor) // => 'x123'
console.log(lastCursor) // => 'x123'
Takes an array of objects and returns a Relay-style connection object.
Example:
import { paginate } from '@good-idea/unwind-edges'
const users = [
{ id: 'abc', name: 'frank' },
{ id: 'def', name: 'ursula' },
{ id: 'ghi', name: 'ira' },
]
const paginatedUsers = paginate(users)
console.log(paginatedUsers)
/**
* {
* edges: [
* {
* cursor: 'abc',
* node: {
* id: 'abc',
* name: 'frank'
* }
* },
* {
* cursor: 'def',
* node: {
* id: 'def',
* name: 'ursula'
* }
* },
* {
* cursor: 'ghi',
* node: {
* id: 'ghi',
* name: 'ira'
* }
* },
* ],
* pageInfo: {
* hasNextPage: false,
* hasPrevPage: false,
* hasPreviousPage: false,
* firstCursor: 'abc',
* lastCursor: 'ghi'
* }
* }
*/
You can also specify first
and after
arguments to return a limited selection of nodes.
const users = [
{ id: 1, name: 'frank' },
{ id: 2, name: 'ursula' },
// ...
{ id: 50, name: 'ira' },
]
const paginated = paginate(users, { first: 10 })
console.log(paginated)
/**
* {
* edges: [
* {
* cursor: 'abc',
* node: {
* id: 'abc',
* name: 'frank'
* }
* },
* {
* cursor: 'def',
* node: {
* id: 'def',
* name: 'ursula'
* }
* },
* // ...
* {
* cursor: 'xyz',
* node: {
* id: 'xyz',
* name: 'fancy'
* }
* },
* ],
* pageInfo: {
* hasNextPage: true,
* hasPrevPage: false,
* hasPreviousPage: false,
* firstCursor: 'abc',
* lastCursor: 'xyz'
* }
* }
*/
In order to make sure that hasNextPage
returns the proper value, be sure to pass in more nodes than you specify as your argument to first
. For instance, when fetching items from a database, a common practice is to overfetch by one.
const users = getUsersFromDB({ first: 11 })
const paginated = paginate(users, { first: 10 })
console.log(paginated.pageInfo.hasNextPage)
// `true` if your database returned more than 10 users
// `false` if your database returned 10 users or less
hasPrevPage
/ hasPreviousPage
will return true
if you specify an after
argument:
const users = getUsersFromDB({ first: 50 })
const paginated = paginate(users, { first: 10, after: 'def' })
console.log(paginated.pageInfo.hasPrevPage) // => true
console.log(paginated.pageInfo.firstCursor) // => 'ghi'
paginate
will automatically look for an id
or _id
property on your node to extract as it's cursor
. If you need to extract a cursor from another property, you can pass in a cursorKey
property in the third parameter:
const users = [
{ id: 'abc', name: 'frank', },
{ id: 'def', name: 'ursula' },
{ id: 'ghi', name: 'ira' }
]
const paginated = paginate(users, {}, { cursorKey: 'name' })
console.log(paginated.edges[0].cursor) // => 'frank'
If you're working in Typescript, you can pass in an additional type argument to tell TS what you're going to get back:
interface User {
id: string
name: string
}
const [users] = unwindEdges<User>(sampleResponse)
console.log(users[0].id) // 👍
console.log(users[0].name) // 👍
console.log(users[0].__cursor) // 👍
console.log(users[0].birthday) // ❗️ Property 'birthday' does not exist on type 'NodeWithCursor<User>'.
The module also exports the Paginated<T>
interface, which can be handy if you want to create an interface for a Relay Connection:
type UserConnection = Paginated<User>