Plugins
Relay Connection
Connection Plugin
The connection plugin provides a new method on the object definition builder, enabling paginated associations between types, following the Relay Connection Specification. It provides simple ways to customize fields available on the Connection, Edges, or PageInfo types.
To install, add the connectionPlugin to the makeSchema.plugins array, along with any other plugins
you'd like to include:
1import { makeSchema, connectionPlugin } from 'nexus'23const schema = makeSchema({4 // ... types, etc,5 plugins: [6 // ... other plugins7 connectionPlugin(),8 ],9})
By default, the plugin will install a t.connectionField method available on the object definition builder:
1export const User = objectType({2 name: "User",3 definition(t) {4 t.connectionField(...);5 },6});
You can change the name of this field by specifying the nexusFieldName in the plugin config.
Usage
There are two main ways to use the connection field, with a nodes property, or a resolve property:
With resolve
If you have custom logic you'd like to provide in resolving the connection, we allow you to instead specify a resolve field, which will make not assumptions about how the edges, cursor, or pageInfo are defined.
You can use this with helpers provided via graphql-relay-js.
1import { connectionFromArray } from 'graphql-relay'23export const usersQueryField = queryField((t) => {4 t.connectionField('users', {5 type: User,6 async resolve(root, args, ctx, info) {7 return connectionFromArray(await ctx.resolveUserNodes(), args)8 },9 })10})
With nodes
When providing a nodes property, we make some assumptions about the structure of the connection. We only
require you return a list of rows to resolve based on the connection, and then we will automatically infer the hasNextPage, hasPreviousPage, and cursor values for you.
1t.connectionField('users', {2 type: User,3 nodes(root, args, ctx, info) {4 // [{ id: 1, ... }, ..., { id: 10, ... }]5 return ctx.users.resolveForConnection(root, args, ctx, info)6 },7})
One limitation of the nodes property, is that you cannot paginate backward without a cursor, or without defining a cursorFromNode property on either the field or plugin config. This is because we can't know how long the connection list may be to begin paginating backward.
1t.connectionField('usersConnectionNodes', {2 type: User,3 cursorFromNode(node, args, ctx, info, { index, nodes }) {4 if (args.last && !args.before) {5 const totalCount = USERS_DATA.length6 return `cursor:${totalCount - args.last! + index + 1}`7 }8 return connectionPlugin.defaultCursorFromNode(node, args, ctx, info, {9 index,10 nodes,11 })12 },13 nodes() {14 // ...15 },16})
Including a nodes field:
If you want to include a nodes field, which includes the nodes of the connection flattened into an array similar to how GitHub does in their GraphQL API, set includeNodesField to true
1connectionPlugin({2 includeNodesField: true,3})
1query IncludeNodesFieldExample {2 users(first: 10) {3 nodes {4 id5 }6 pageInfo {7 endCursor8 hasNextPage9 }10 }11}
Top level connection field
The queryField or mutationField helpers may accept a function rather than a field name, which will be shorthand for the query builder:
1export const usersField = queryField((t) => {2 t.connectionField('users', {3 type: Users,4 nodes(root, args, ctx, info) {5 return ctx.users.forConnection(root, args)6 },7 })8})
There are properties on the plugin to help configure this including, cursorFromNode, which allows you to customize how the cursor is created, or pageInfoFromNodes to customize how hasNextPage or hasPreviousPage are set.
Pagination Arguments
Modifying arguments
You may specify additionalArgs on either the plugin or the field config, to add additional arguments to the connection:
1t.connectionField('userConnectionAdditionalArgs', {2 type: User,3 disableBackwardPagination: true,4 additionalArgs: {5 isEven: booleanArg({6 description: 'If true, filters the users with an odd pk',7 }),8 },9 resolve() {10 // ...11 },12})
If you have specified args on the field, they will overwrite any custom args defined on the plugin config, unless inheritAdditionalArgs is set to true.
Disabling forward/backward pagination
By default we assume that the cursor can paginate in both directions. This is not always something every
API needs or supports, so to turn them off, you can set disableForwardPagination, or disableBackwardPagination to
true on either the paginationConfig, or on the fieldConfig.
When we disable the forward or backward pagination args, by default we set the remaining first or last to required.
If you do not want this to happen, specify strictArgs: false in the plugin or field config.
Argument validation
By default, the connection field validates that a first or a last must be provided, and not both. If you wish to provide your own validation, supply a validateArgs property to either the connectionPlugin config, or to the field configuration directly.
1connectionPlugin({2 validateArgs(args, info) {3 // ... custom validate logic4 },5})67// or89t.connectionField('users', {10 // ...11 validateArgs: (args, info) => {12 // custom validate logic13 },14})
Extending Connection / Edge types
There are two ways to extend the connection type, one is by providing extendConnection on the connectionPlugin configuration, the other is to add an extendConnection or extendEdge definition block on the field config.
Globally
1connectionPlugin({2 extendConnection: {3 totalCount: { type: 'Int' },4 },5})67t.connectionField('users', {8 type: User,9 nodes: () => {10 // ...11 },12 totalCount() {13 return ctx.users.totalCount(args)14 },15})
One-off / per-field
1t.connectionField('users', {2 extendConnection(t) {3 t.int('totalCount', {4 resolve: (source, args, ctx) => ctx.users.totalCount(args),5 })6 },7})
The field-level customization approach will result in a custom connection type specific to that type/field, e.g. QueryUsers_Connection, since the modification is specific to the individual field.
Multiple Connection Types
You can create multiple field connection types with varying defaults, available under different connections builder methods. A typePrefix property should be supplied to configure the name
Custom Usage:
1import { makeSchema, connectionPlugin } from 'nexus'23const schema = makeSchema({4 // ... types, etc,5 plugins: [6 connectionPlugin({7 typePrefix: 'Analytics',8 nexusFieldName: 'analyticsConnection',9 extendConnection: {10 totalCount: { type: 'Int' },11 avgDuration: { type: 'Int' },12 },13 }),14 connectionPlugin({}),15 ],16})
Custom names for Connection / Edge types
You can provide a function to generate a custom name for connection and edge types. The function will receive the field and parent type names.
Globally
1connectionPlugin({2 getConnectionName(fieldName, parentTypeName) {3 return `${parentTypeName}${upperFirst(fieldName)}Connection`4 },5 getEdgeName(fieldName, parentTypeName) {6 return `${parentTypeName}${upperFirst(fieldName)}Edge`7 },8})
One-off / per-field
1t.connectionField('users', {2 getConnectionName(fieldName, parentTypeName) {3 return `${parentTypeName}${upperFirst(fieldName)}Connection`4 },5 getEdgeName(fieldName, parentTypeName) {6 return `${parentTypeName}${upperFirst(fieldName)}Edge`7 },8})