@pothos/plugin-relay
Advanced tools
Comparing version 3.12.1 to 3.13.0
# Change Log | ||
## 3.13.0 | ||
### Minor Changes | ||
- 79e69c2b: Add resolveCursorConnection helper for relay plugin | ||
## 3.12.1 | ||
@@ -4,0 +10,0 @@ |
@@ -8,2 +8,14 @@ import { MaybePromise, SchemaTypes } from '@pothos/core'; | ||
} | ||
export interface ResolveCursorConnectionOptions<T> { | ||
args: DefaultConnectionArguments; | ||
defaultSize?: number; | ||
maxSize?: number; | ||
toCursor: (value: T) => string; | ||
} | ||
export interface ResolveCursorConnectionArgs { | ||
before?: string; | ||
after?: string; | ||
limit: number; | ||
inverted: boolean; | ||
} | ||
interface ResolveArrayConnectionOptions { | ||
@@ -27,3 +39,5 @@ args: DefaultConnectionArguments; | ||
}, false>; | ||
declare type NodeType<T> = T extends Promise<(infer N)[] | null> | (infer N)[] | null ? N : never; | ||
export declare function resolveCursorConnection<U extends Promise<unknown[] | null> | unknown[] | null>(options: ResolveCursorConnectionOptions<NodeType<U>>, resolve: (params: ResolveCursorConnectionArgs) => U): Promise<ConnectionShape<SchemaTypes, NodeType<U>, false, false, false>>; | ||
export {}; | ||
//# sourceMappingURL=connections.d.ts.map |
@@ -99,2 +99,61 @@ const OFFSET_CURSOR_PREFIX = "OffsetConnection:"; | ||
} | ||
function parseCurserArgs(options) { | ||
const { before, after, first, last } = options.args; | ||
var _defaultSize; | ||
const defaultSize = (_defaultSize = options.defaultSize) !== null && _defaultSize !== void 0 ? _defaultSize : DEFAULT_SIZE; | ||
var _maxSize; | ||
const maxSize = (_maxSize = options.maxSize) !== null && _maxSize !== void 0 ? _maxSize : DEFAULT_MAX_SIZE; | ||
if (first != null && first < 0) { | ||
throw new TypeError("Argument \"first\" must be a non-negative integer"); | ||
} | ||
if (last != null && last < 0) { | ||
throw new Error("Argument \"last\" must be a non-negative integer"); | ||
} | ||
var ref; | ||
const limit = Math.min((ref = first !== null && first !== void 0 ? first : last) !== null && ref !== void 0 ? ref : defaultSize, maxSize) + 1; | ||
const inverted = after ? !!last && !first : !!before && !first || !first && !!last; | ||
return { | ||
before: before !== null && before !== void 0 ? before : void 0, | ||
after: after !== null && after !== void 0 ? after : void 0, | ||
limit, | ||
expectedSize: limit - 1, | ||
inverted, | ||
hasPreviousPage: (resultSize) => !!after || resultSize >= limit && !first, | ||
hasNextPage: (resultSize) => !!before || !last && resultSize >= limit | ||
}; | ||
} | ||
export async function resolveCursorConnection(options, resolve) { | ||
var ref, ref1; | ||
const { before, after, limit, inverted, expectedSize, hasPreviousPage, hasNextPage } = parseCurserArgs(options); | ||
const nodes = await resolve({ | ||
before, | ||
after, | ||
limit, | ||
inverted | ||
}); | ||
if (!nodes) { | ||
return nodes; | ||
} | ||
const trimmed = nodes.slice(0, expectedSize); | ||
if (inverted) { | ||
trimmed.reverse(); | ||
} | ||
const edges = trimmed.map((value) => value == null ? null : { | ||
cursor: options.toCursor(value), | ||
node: value | ||
}); | ||
var _after, ref2; | ||
const startCursor = edges.length > 0 ? (ref = edges[0]) === null || ref === void 0 ? void 0 : ref.cursor : (ref2 = (_after = options.args.after) !== null && _after !== void 0 ? _after : options.args.before) !== null && ref2 !== void 0 ? ref2 : ""; | ||
var _after1, ref3; | ||
const endCursor = edges.length > 0 ? (ref1 = edges[edges.length - 1]) === null || ref1 === void 0 ? void 0 : ref1.cursor : (ref3 = (_after1 = options.args.after) !== null && _after1 !== void 0 ? _after1 : options.args.before) !== null && ref3 !== void 0 ? ref3 : ""; | ||
return { | ||
edges: edges, | ||
pageInfo: { | ||
startCursor, | ||
endCursor, | ||
hasPreviousPage: hasPreviousPage(nodes.length), | ||
hasNextPage: hasNextPage(nodes.length) | ||
} | ||
}; | ||
} | ||
//# sourceMappingURL=connections.js.map |
@@ -9,2 +9,3 @@ "use strict"; | ||
exports.resolveArrayConnection = resolveArrayConnection; | ||
exports.resolveCursorConnection = resolveCursorConnection; | ||
const OFFSET_CURSOR_PREFIX = 'OffsetConnection:'; | ||
@@ -108,3 +109,62 @@ const DEFAULT_MAX_SIZE = 100; | ||
} | ||
function parseCurserArgs(options) { | ||
const { before , after , first , last } = options.args; | ||
var _defaultSize; | ||
const defaultSize = (_defaultSize = options.defaultSize) !== null && _defaultSize !== void 0 ? _defaultSize : DEFAULT_SIZE; | ||
var _maxSize; | ||
const maxSize = (_maxSize = options.maxSize) !== null && _maxSize !== void 0 ? _maxSize : DEFAULT_MAX_SIZE; | ||
if (first != null && first < 0) { | ||
throw new TypeError('Argument "first" must be a non-negative integer'); | ||
} | ||
if (last != null && last < 0) { | ||
throw new Error('Argument "last" must be a non-negative integer'); | ||
} | ||
var ref; | ||
const limit = Math.min((ref = first !== null && first !== void 0 ? first : last) !== null && ref !== void 0 ? ref : defaultSize, maxSize) + 1; | ||
const inverted = after ? !!last && !first : !!before && !first || !first && !!last; | ||
return { | ||
before: before !== null && before !== void 0 ? before : void 0, | ||
after: after !== null && after !== void 0 ? after : void 0, | ||
limit, | ||
expectedSize: limit - 1, | ||
inverted, | ||
hasPreviousPage: (resultSize)=>!!after || resultSize >= limit && !first, | ||
hasNextPage: (resultSize)=>!!before || !last && resultSize >= limit | ||
}; | ||
} | ||
async function resolveCursorConnection(options, resolve) { | ||
var ref, ref1; | ||
const { before , after , limit , inverted , expectedSize , hasPreviousPage , hasNextPage } = parseCurserArgs(options); | ||
const nodes = await resolve({ | ||
before, | ||
after, | ||
limit, | ||
inverted | ||
}); | ||
if (!nodes) { | ||
return nodes; | ||
} | ||
const trimmed = nodes.slice(0, expectedSize); | ||
if (inverted) { | ||
trimmed.reverse(); | ||
} | ||
const edges = trimmed.map((value)=>value == null ? null : { | ||
cursor: options.toCursor(value), | ||
node: value | ||
}); | ||
var _after, ref2; | ||
const startCursor = edges.length > 0 ? (ref = edges[0]) === null || ref === void 0 ? void 0 : ref.cursor : (ref2 = (_after = options.args.after) !== null && _after !== void 0 ? _after : options.args.before) !== null && ref2 !== void 0 ? ref2 : ''; | ||
var _after1, ref3; | ||
const endCursor = edges.length > 0 ? (ref1 = edges[edges.length - 1]) === null || ref1 === void 0 ? void 0 : ref1.cursor : (ref3 = (_after1 = options.args.after) !== null && _after1 !== void 0 ? _after1 : options.args.before) !== null && ref3 !== void 0 ? ref3 : ''; | ||
return { | ||
edges: edges, | ||
pageInfo: { | ||
startCursor, | ||
endCursor, | ||
hasPreviousPage: hasPreviousPage(nodes.length), | ||
hasNextPage: hasNextPage(nodes.length) | ||
} | ||
}; | ||
} | ||
//# sourceMappingURL=connections.js.map |
{ | ||
"name": "@pothos/plugin-relay", | ||
"version": "3.12.1", | ||
"version": "3.13.0", | ||
"description": "A Pothos plugin for adding relay style connections, nodes, and cursor based pagination to your GraphQL schema", | ||
@@ -40,3 +40,3 @@ "main": "./lib/index.js", | ||
"devDependencies": { | ||
"@pothos/core": "3.11.1", | ||
"@pothos/core": "3.12.0", | ||
"@pothos/test-utils": "1.2.2", | ||
@@ -43,0 +43,0 @@ "graphql": "16.5.0", |
@@ -97,3 +97,3 @@ # Relay Plugin | ||
resolve(parent, args) { | ||
console.log(`Get request for type ${args.id.type} with id ${args.id.typename}`); | ||
console.log(`Get request for type ${args.id.typename} with id ${args.id.id}`); | ||
return true; | ||
@@ -175,19 +175,21 @@ }, | ||
resolve: (parent, { first, last, before, after }) => { | ||
return resolveOffsetConnection({ args }, ({ limit, offset }) => { | ||
return { | ||
pageInfo: { | ||
hasNextPage: false, | ||
hasPreviousPage: false, | ||
startCursor: 'abc', | ||
endCursor: 'def', | ||
return { | ||
pageInfo: { | ||
hasNextPage: false, | ||
hasPreviousPage: false, | ||
startCursor: 'abc', | ||
endCursor: 'def', | ||
}, | ||
edges: [ | ||
{ | ||
cursor: 'abc', | ||
node: new NumberThing(123), | ||
}, | ||
edges: [ | ||
{ | ||
cursor: 'xyz', | ||
node: new NumberThing(123), | ||
}, | ||
], | ||
}; | ||
}); | ||
}, | ||
{ | ||
cursor: 'def', | ||
node: new NumberThing(123), | ||
}, | ||
] | ||
} | ||
}), | ||
}, | ||
@@ -261,4 +263,38 @@ { | ||
I am planning to add more helpers in the future. | ||
Cursor based pagination can be implemented using the `resolveCursorConnection` method. The following | ||
example uses prisma, but a similar solution should work with any data store that supports limits, | ||
ordering, and filtering. | ||
```typescript | ||
import { resolveCursorConnection, ResolveCursorConnectionArgs } from '@pothos/plugin-relay'; | ||
builder.queryField('posts', (t) => | ||
t.connection({ | ||
type: Post, | ||
resolve: (_, args) => | ||
resolveCursorConnection( | ||
{ | ||
args, | ||
toCursor: (post) => post.createdAt.toISOString(), | ||
}, | ||
// Manually defining the arg type here is required | ||
// so that typescript can correctly infer the return value | ||
({ before, after, limit, inverted }: ResolveCursorConnectionArgs) => | ||
prisma.post.findMany({ | ||
take: limit, | ||
where: { | ||
createdAt: { | ||
lt: before, | ||
gt: after, | ||
}, | ||
}, | ||
orderBy: { | ||
createdAt: inverted ? 'desc' : 'asc', | ||
}, | ||
}), | ||
), | ||
}), | ||
); | ||
``` | ||
### Relay Mutations | ||
@@ -265,0 +301,0 @@ |
@@ -10,2 +10,16 @@ import { MaybePromise, SchemaTypes } from '@pothos/core'; | ||
export interface ResolveCursorConnectionOptions<T> { | ||
args: DefaultConnectionArguments; | ||
defaultSize?: number; | ||
maxSize?: number; | ||
toCursor: (value: T) => string; | ||
} | ||
export interface ResolveCursorConnectionArgs { | ||
before?: string; | ||
after?: string; | ||
limit: number; | ||
inverted: boolean; | ||
} | ||
interface ResolveArrayConnectionOptions { | ||
@@ -166,1 +180,79 @@ args: DefaultConnectionArguments; | ||
} | ||
function parseCurserArgs(options: ResolveOffsetConnectionOptions) { | ||
const { before, after, first, last } = options.args; | ||
const defaultSize = options.defaultSize ?? DEFAULT_SIZE; | ||
const maxSize = options.maxSize ?? DEFAULT_MAX_SIZE; | ||
if (first != null && first < 0) { | ||
throw new TypeError('Argument "first" must be a non-negative integer'); | ||
} | ||
if (last != null && last < 0) { | ||
throw new Error('Argument "last" must be a non-negative integer'); | ||
} | ||
const limit = Math.min(first ?? last ?? defaultSize, maxSize) + 1; | ||
const inverted = after ? !!last && !first : (!!before && !first) || (!first && !!last); | ||
return { | ||
before: before ?? void 0, | ||
after: after ?? void 0, | ||
limit, | ||
expectedSize: limit - 1, | ||
inverted, | ||
hasPreviousPage: (resultSize: number) => !!after || (resultSize >= limit && !first), | ||
hasNextPage: (resultSize: number) => !!before || (!last && resultSize >= limit), | ||
}; | ||
} | ||
type NodeType<T> = T extends Promise<(infer N)[] | null> | (infer N)[] | null ? N : never; | ||
export async function resolveCursorConnection< | ||
U extends Promise<unknown[] | null> | unknown[] | null, | ||
>( | ||
options: ResolveCursorConnectionOptions<NodeType<U>>, | ||
resolve: (params: ResolveCursorConnectionArgs) => U, | ||
): Promise<ConnectionShape<SchemaTypes, NodeType<U>, false, false, false>> { | ||
const { before, after, limit, inverted, expectedSize, hasPreviousPage, hasNextPage } = | ||
parseCurserArgs(options); | ||
const nodes = (await resolve({ before, after, limit, inverted })) as NodeType<U>[] | null; | ||
if (!nodes) { | ||
return nodes as never; | ||
} | ||
const trimmed = nodes.slice(0, expectedSize); | ||
if (inverted) { | ||
trimmed.reverse(); | ||
} | ||
const edges = trimmed.map((value) => | ||
value == null | ||
? null | ||
: { | ||
cursor: options.toCursor(value), | ||
node: value, | ||
}, | ||
); | ||
const startCursor = | ||
edges.length > 0 ? edges[0]?.cursor : options.args.after ?? options.args.before ?? ''; | ||
const endCursor = | ||
edges.length > 0 | ||
? edges[edges.length - 1]?.cursor | ||
: options.args.after ?? options.args.before ?? ''; | ||
return { | ||
edges: edges as never, | ||
pageInfo: { | ||
startCursor, | ||
endCursor, | ||
hasPreviousPage: hasPreviousPage(nodes.length), | ||
hasNextPage: hasNextPage(nodes.length), | ||
}, | ||
}; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
354868
4038
674