Connection Spec
The Connection Spec is an opinionated formal specification for a convention of how to expose a paginated list of items in a GraphQL schema. Smart clients like Relay are able to use this specification to automatically handle pagination for you.
While Connections are sometimes refered to as "Relay Connections", they are not specific to Relay and can be considered a best practice for any GraphQL API.
Given that most application that we build are heavily oriented around lists of data, having a consistent way to interact with paginated lists allows clients to build helpful, optimized, abstractions for things like infinite scrolling, pagination, prelaoding, and more.
When possible, prefer modeling list fields using connections, even if your server does not implement pagination yet. This will make it easier to add pagination in the future without breaking clients.
The npm library graphql-relay
provides a number of helpful utility functions which make it easy to implement simple connection fields even from simple arrays.
Example Connection using Grats
Grats' support for generic types is a perfect fit for modeling connections. Here's an example of a generic, reusable Connection
type. Feel free to copy/paste these reusable types into your own code base.
For a full working example of Connections in action, see our Production App example app.
- TypeScript
- GraphQL
import { Int } from "grats";
/** @gqlField */
export function users(
_: Query,
args: {
first?: Int | null;
after?: string | null;
last?: Int | null;
before?: string | null;
},
): Connection<User> {
const users = [{ name: "John" }];
return connectionFromArray(users, args);
}
/** --- Reusable Connection Types --- */
/** @gqlType */
export type Connection<T> = {
/** @gqlField */
edges: Edge<T>[];
/** @gqlField */
pageInfo: PageInfo;
};
/** @gqlType */
export type Edge<T> = {
/** @gqlField */
node: T;
/** @gqlField */
cursor: string;
};
/** @gqlType */
export type PageInfo = {
/** @gqlField */
startCursor: string | null;
/** @gqlField */
endCursor: string | null;
/** @gqlField */
hasNextPage: boolean;
/** @gqlField */
hasPreviousPage: boolean;
};
/** @gqlType */
type Query = unknown;
/** @gqlType */
type User = {
/** @gqlField */
name: string;
};
// This function can be found in the module `graphql-relay`.
// Extracted here for example purposes.
declare function connectionFromArray<T>(
data: ReadonlyArray<T>,
args: {
first?: Int | null;
after?: string | null;
last?: Int | null;
before?: string | null;
},
): Connection<T>;
type PageInfo {
endCursor: String
hasNextPage: Boolean
hasPreviousPage: Boolean
startCursor: String
}
type Query {
users(after: String, before: String, first: Int, last: Int): UserConnection
}
type User {
name: String
}
type UserConnection {
edges: [UserEdge!]
pageInfo: PageInfo
}
type UserEdge {
cursor: String
node: User
}