Please add example how to do a GraphQL for Relay with connections, edges, nodes, totalCount...
https://facebook.github.io/relay/docs/en/graphql-server-specification.html
Have you figured out any pattern to implement Relay specification?
i would be interested in this issue as well!
Some community projects:
nestjs-graphql-relay
npm package (https://github.com/piic/nestjs-plugins#readme) by @piic. (currently has a hard dependency on TypeORM)
@ObjectType({ isAbstract: true })
abstract class RecipesEdge implements Relay.Edge<Recipe> {
@Field(() => Recipe)
readonly node!: Recipe;
@Field()
readonly cursor!: Relay.ConnectionCursor;
}
@ObjectType()
export class RecipesConnection implements Relay.Connection<Recipe> {
@Field()
readonly pageInfo!: PageInfo;
@Field(() => [RecipesEdge])
readonly edges!: Array<Relay.Edge<Recipe>>;
@Field(() => Aggregate)
readonly aggregate: Aggregate;
}
Other: https://github.com/kazekyo/nestjs-graphql-relay example by @kazekyo (last used with NestJS 6)
Since NestJS recently incorporated its own GraphQL library (instead of using type-graphql), I think NestJS should also explicitly (but optional) support Relay connections. Most likely in the spirit of https://github.com/wemaintain/auto-relay by @wemaintain (reference: https://github.com/MichalLytek/type-graphql/issues/142#issuecomment-582132843).
This is my approach inspired by (but not using the entire) https://www.npmjs.com/package/nestjs-graphql-relay:
package.json
{
"dependencies": {
/* ... */
"@nestjs/graphql": "^7.2.0",
"@types/graphql-relay": "^0.4.11",
"apollo-server-fastify": "^2.11.0",
"graphql": "<15.0.0",
"graphql-relay": "^0.6.0",
"graphql-tools": "^4.0.7",
}
}
nestjs-graphql-relay.ts
import { Field, ObjectType, Int, ID, InterfaceType } from '@nestjs/graphql';
import * as Relay from 'graphql-relay';
@InterfaceType()
export abstract class Node {
@Field(() => ID)
id!: string;
}
@ObjectType({ isAbstract: true })
export abstract class Aggregate {
@Field(() => Int)
readonly count: number;
}
@ObjectType()
export class PageInfo implements Relay.PageInfo {
@Field(() => Boolean, { nullable: true })
hasNextPage?: boolean;
@Field(() => Boolean, { nullable: true })
hasPreviousPage?: boolean;
@Field(() => String, { nullable: true })
startCursor?: Relay.ConnectionCursor;
@Field(() => String, { nullable: true })
endCursor?: Relay.ConnectionCursor;
}
project.service.ts
import { Field, Int, ObjectType, ID } from '@nestjs/graphql';
import { Node } from '../nestjs-graphql-relay';
@ObjectType({implements: Node})
export class Project extends Node {
@prop({alias: 'id'})
_id?: string;
@Field(() => ID)
id!: string;
@IsString()
@prop()
@Field()
name!: string;
@IsString()
@prop()
@Field()
slug!: string;
}
project.resolver.ts
import { Resolver, Args, Query, ID, Mutation, Field, ObjectType, ResolveField, Parent, Int } from '@nestjs/graphql';
import { Project } from './project.entity';
import { ProjectService } from './project.service';
import * as Relay from 'graphql-relay';
import { PageInfo, Aggregate } from './nestjs-graphql-relay';
@ObjectType({isAbstract: true})
abstract class ProjectsEdge implements Relay.Edge<Project> {
@Field(() => Project)
readonly node!: Project;
@Field()
readonly cursor!: Relay.ConnectionCursor;
}
@ObjectType()
export class ProjectsConnection implements Relay.Connection<Project> {
@Field()
readonly pageInfo!: PageInfo;
@Field(() => [ProjectsEdge])
readonly edges!: Relay.Edge<Project>[];
@Field(() => Aggregate, {nullable: true})
readonly aggregate?: Aggregate;
}
@Resolver(() => Project)
export class ProjectResolver {
constructor(
private readonly projectService: ProjectService,
) {}
@Query(returns => ProjectsConnection)
async projects(): Promise<ProjectsConnection> {
const edges = (await this.projectService.findAll())
.map(_ => ({node: _, cursor: _.id} as ProjectsEdge));
const startCursor = edges.length >= 1 ? edges[0].cursor : undefined;
const endCursor = edges.length >= 1 ? edges[edges.length - 1].cursor : undefined;
return {
pageInfo: {hasPreviousPage: false, hasNextPage: false, startCursor, endCursor},
edges};
}
}
I created a repository with a relay-compliant GraphQL server using TypeGraphQL and TypeORM. It covers things such as global object identification and bi-directional relay cursor pagination (following the specification).
I believe that it could be easily adapted to NestJS.
https://github.com/calmonr/typegraphql-relay
It's a boilerplate, not a library.
It has room for improvement (ordering, filtering, dataloader, more examples, etc) but most important parts are implemented and working properly.
I really liked the @Superd22 (auto-relay) implementation but it was inspired by @kazekyo and @9renpoto that has an issue when paginating. The user must specify before when paginating backwards (aka: a weird corner case). (good job tho)
Feel free to send suggestion, issues, pull requests.
Thank you.
cc @ceefour @johannesschobel @Simonpedro @igo @GrayStrider @mattleff @icereval @hugohernani
Most helpful comment
This is my approach inspired by (but not using the entire) https://www.npmjs.com/package/nestjs-graphql-relay:
package.json
nestjs-graphql-relay.ts
project.service.ts
project.resolver.ts