Skip to main content

Relations

When using the nestjs-query you can specify relations that should be exposed for the DTO using the following decorators.

  • @Relation - A relation that is a single value (one-to-one, many-to-one)
  • @FilterableRelation - A @Relation that enables filtering the parent by fields of the relation DTO.
  • @UnPagedRelation - An array of relations (e.g, many-to-many, one-to-many) that returns all of the related records.
  • @FilterableUnPagedRelation - An @UnPagedRelation that enables filtering the parent by fields of the relation DTO.
  • @OffsetConnection - A connection that represents a collection (e.g, many-to-many, one-to-many) that uses offset based pagination.
  • @FilterableOffsetConnection - An @OffsetConnection that enables filtering the parent by fields of the connection DTO.
  • @CursorConnection - A connection that represents a collection (e.g, many-to-many, one-to-many) that uses cursor based pagination.
  • @FilterableCursorConnection - A @CursorConnection that enables filtering the parent by fields of the connection DTO.
warning

@FilterableUnPagedRelation, @FilterableOffsetConnection, and @FilterableCursorConnection are not supported by mongoose!

note

When loading relations a dataloader that is scoped to the request will be used. This prevents the n+1 problem.

In the following examples we are going to use the following entities.

todo-item/todo-item.entity.ts
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, OneToMany } from 'typeorm';import { SubTaskEntity } from '../sub-task/sub-task.entity';
@Entity()export class TodoItemEntity {  @PrimaryGeneratedColumn()  id!: string;
  @Column()  title!: string;
  @Column()  completed!: boolean;
  @OneToMany(    () => SubTaskEntity,    subTask => subTask.todoItem,  )  subTasks!: SubTaskEntity[];
  @CreateDateColumn()  created!: Date;
  @UpdateDateColumn()  updated!: Date;}

@Relation#

A relation that is a single value (one-to-one, many-to-one)

Example#

Based on the entities defined above we can add a todoItem relation to the SubTask by creating the following SubTaskDTO with a @Relation decorator.

sub-task/sub-task.dto.ts
import { FilterableField, IDField, Relation } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';import { TodoItemDTO } from '../todo-item/todo-item.dto.ts';
@ObjectType('SubTask')@Relation('todoItem', () => TodoItemDTO, { disableRemove: true })export class SubTaskDTO {  @FilterableField(() => ID)  id!: string;
  @FilterableField()  title!: string;
  @FilterableField()  completed!: boolean;
  @FilterableField(() => GraphQLISODateTime)  created!: Date;
  @FilterableField(() => GraphQLISODateTime)  updated!: Date;
  @FilterableField()  todoItemId!: string;}

The @Relation decorator lets nestjs-query know to expose the following endpoints:

  • subTask.todoItem - A query to retrieve the SubTasks TodoItem
  • setTodoItemOnSubTask - A mutation to set the TodoItem on a SubTask.
  • removeTodoItemFromSubTask - A mutation to remove a TodoItem/SubTask relation.
    • NOTE This does not typically remove either record just removes the relation.
note

In this example we disableRemove, because todoItem is not nullable, by specifying the disableRemove option.

To set up the resolver you can use the NestjsQueryGraphQLModule.

sub-task/sub-task.module.ts
import { NestjsQueryGraphQLModule } from '@nestjs-query/query-graphql';import { NestjsQueryTypeOrmModule } from '@nestjs-query/query-typeorm';import { Module } from '@nestjs/common';import { SubTaskDTO } from './sub-task.dto';import { SubTaskEntity } from './sub-task.entity';
@Module({  imports: [    NestjsQueryGraphQLModule.forFeature({      imports: [NestjsQueryTypeOrmModule.forFeature([SubTaskEntity])],      resolvers: [{ DTOClass: SubTaskDTO, EntityClass: SubTaskEntity }],    }),  ],})export class SubTaskModule {}

@nestjs-query/query-graphql will then automatically create the following graphql definition:

type SubTask {  id: ID!  title: String!  completed: Boolean!  created: DateTime!  updated: DateTime!  todoItemId: String!  todoItem: TodoItem!}

The following mutations will also be automatically exposed.

type Mutation {  setTodoItemOnSubTask(input: RelationInput!): SubTask!}
input RelationInput {  id: ID!  relationId: ID!}
note

If disableRemove was set to false or not specified a removeTodoItemFromSubTask mutation would also be exposed with the same arguments as setTodoItemOnSubTask.

@FilterableRelation#

The @FilterableRelation extends the @Relation decorator exposing the ability to filter the DTO that defines the relation by relation properties.

warning

The @FilterableRelation decorator will only work with relations defined by the orm used (e.g. typeorm, sequelize). If your relations are federated or you are using mongoose you cannot use the @FilterableRelation decorator.

Example#

In this example we'll use the same Entities defined above to create a graphql endpoint that allows filtering SubTasks by TodoItems.

sub-task/sub-task.dto.ts
import { FilterableField, IDField, FilterableRelation } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';import { TodoItemDTO } from '../todo-item/todo-item.dto.ts';
@ObjectType('SubTask')@FilterableRelation('todoItem', () => TodoItemDTO, { disableRemove: true })export class SubTaskDTO {  @IDField(() => ID)  id!: string;
  @FilterableField()  title!: string;
  @FilterableField()  completed!: boolean;
  @FilterableField(() => GraphQLISODateTime)  created!: Date;
  @FilterableField(() => GraphQLISODateTime)  updated!: Date;
  @FilterableField(() => ID)  todoItemId!: string;}

Notice the use of @FilterableRelation instead of @Relation, by using the @FilterableRelation version nestjs-query will allow filtering on the todoItem relation.

The module definition remains the same.

sub-task/sub-task.module.ts
import { NestjsQueryGraphQLModule } from '@nestjs-query/query-graphql';import { NestjsQueryTypeOrmModule } from '@nestjs-query/query-typeorm';import { Module } from '@nestjs/common';import { SubTaskDTO } from './sub-task.dto';import { SubTaskEntity } from './sub-task.entity';
@Module({  imports: [    NestjsQueryGraphQLModule.forFeature({      imports: [NestjsQueryTypeOrmModule.forFeature([SubTaskEntity])],      resolvers: [{ DTOClass: SubTaskDTO, EntityClass: SubTaskEntity }],    }),  ],})export class SubTaskModule {}

When querying for SubTasks you can now also filter on todoItem properties.

In this example we'll find all subTasks that are related to a todoItem with a title that starts with Created.

{  subTasks(filter: { todoItem: { title: { like: "Create%" } } }) {    title    completed  }}

@UnPagedRelation#

You can also use the @UnPagedRelation decorator to define a relation that does not use paging and returns an array of all the related records.

Example#

Based on the entity definition above we can define a TodoItemDTO with a subTasks relation.

todo-item/todo-item.dto.ts
import { FilterableField, IDField, UnPagedRelation } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';import { SubTaskDTO } from '../sub-task/sub-task.dto'
@ObjectType('TodoItem')@UnPagedRelation('subTasks', () => SubTaskDTO, { disableRemove: true })export class TodoItemDTO {  @IDField(() => ID)  id!: string;
  @FilterableField()  title!: string;
  @FilterableField()  completed!: boolean;
  @FilterableField(() => GraphQLISODateTime)  created!: Date;
  @FilterableField(() => GraphQLISODateTime)  updated!: Date;}

When specifying a many relation a couple of endpoints will automatically be generated. In this example the following are generated.

  • todoItem.subTasks - A query endpoint to retrieve a TodoItems SubTasks.
    • The subTasks property will accept a query to allow you to filter, and sort results.
    • The subTasks will be returned as an array of results.
  • addSubTasksToTodoItem - A mutation to add SubTasks to a TodoItem.
note

In this example we disableRemove because SubTasks cannot exist without a TodoItem.

To set up the resolver you can use the NestjsQueryGraphQLModule.

todo-item/todo-item.module.ts
import { NestjsQueryGraphQLModule } from '@nestjs-query/query-graphql';import { NestjsQueryTypeOrmModule } from '@nestjs-query/query-typeorm';import { Module } from '@nestjs/common';import { TodoItemDTO } from './todo-item.dto';import { TodoItemEntity } from './todo-item.entity';
@Module({  imports: [    NestjsQueryGraphQLModule.forFeature({      imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],      resolvers: [{ DTOClass: TodoItemDTO, EntityClass: TodoItemEntity }],    }),  ],})export class TodoItemModule {}

The generated schema will contain a TodoItem type like the following.

type TodoItem {  id: ID!  title: String!  completed: Boolean!  created: DateTime!  updated: DateTime!  subTasks(    filter: SubTaskFilter = {}    sorting: [SubTaskSort!] = []  ): [SubTask!]!}

The following mutations will also be automatically exposed.

type Mutation {   addSubTasksToTodoItem(input: RelationsInput!): TodoItem!}
input RelationsInput {  id: ID!  relationIds: [ID!]!}
note

If disableRemove was set to false or not specified a removeSubTasksFromTodoItem mutation would also be exposed with the same arguments as addSubTasksToTodoItem.

@FilterableUnPagedRelation#

The @FilterableUnPagedRelation extends the @UnPagedRelation decorator exposing the ability to filter the DTO that defines the relation by relation properties.

warning

The @FilterableUnPagedRelation decorator will only work with relations defined by the orm used (e.g. typeorm, sequelize). If your relations are federated or you are using mongoose you cannot use the @FilterableUnPagedRelation decorator.

Example#

In this example we'll use the same Entities defined above to create a graphql endpoint that allows filtering TodoItems by SubTasks.

sub-task/sub-task.dto.ts
import { FilterableField, IDField, FilterableUnPagedRelation } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';import { SubTaskDTO } from '../sub-task/sub-task.dto'
@ObjectType('TodoItem')@FilterableUnPagedRelation('subTasks', () => SubTaskDTO, { disableRemove: true })export class TodoItemDTO {  @IDField(() => ID)  id!: string;
  @FilterableField()  title!: string;
  @FilterableField()  completed!: boolean;
  @FilterableField(() => GraphQLISODateTime)  created!: Date;
  @FilterableField(() => GraphQLISODateTime)  updated!: Date;}

Notice the use of @FilterableUnPagedRelation instead of @UnPagedRelation, by using the @FilterableUnPagedRelation version nestjs-query will allow filtering on the subTasks relation.

The module definition remains the same.

sub-task/sub-task.module.ts
import { NestjsQueryGraphQLModule } from '@nestjs-query/query-graphql';import { NestjsQueryTypeOrmModule } from '@nestjs-query/query-typeorm';import { Module } from '@nestjs/common';import { TodoItemDTO } from './todo-item.dto';import { TodoItemEntity } from './todo-item.entity';
@Module({  imports: [    NestjsQueryGraphQLModule.forFeature({      imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],      resolvers: [{ DTOClass: TodoItemDTO, EntityClass: TodoItemEntity }],    }),  ],})export class TodoItemModule {}

When querying for TodoItems you can now also filter on subTasks properties.

In this example we'll find all todoItems that are related to a subTasks that are completed.

{  todoItems(filter: { subTasks: { completed: { is: true } } }) {    pageInfo {      hasNextPage      hasPreviousPage      startCursor      endCursor    }    edges {      node {        id        title        description        completed        subTasks {          title          description          completed        }      }      cursor    }  }}

@OffsetConnection#

Example#

Based on the entity definitions above we can create a TodoItemDTO with a connection to the subTasks.

todo-item/todo-item.dto.ts
import { FilterableField, IDField, OffsetConnection } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';import { SubTaskDTO } from '../sub-task/sub-task.dto'
@ObjectType('TodoItem')@OffsetConnection('subTasks', () => SubTaskDTO, { disableRemove: true })export class TodoItemDTO {  @IDField(() => ID)  id!: string;
  @FilterableField()  title!: string;
  @FilterableField()  completed!: boolean;
  @FilterableField(() => GraphQLISODateTime)  created!: Date;
  @FilterableField(() => GraphQLISODateTime)  updated!: Date;}

When specifying a @OffsetConnection relation a couple of endpoints will automatically be generated. In this example the following are generated.

  • todoItem.subTasks - A query to retrieve a TodoItems SubTasks.
    • The subTasks property will accept a query to allow you to filter, page and sort results.
    • The subTasks property will return a offset based connection to page through results.
  • addSubTasksToTodoItem - A mutation to add SubTasks to a TodoItem.
note

In this example we disableRemove because SubTasks cannot exist without a TodoItem.

To set up the resolver you can use the NestjsQueryGraphQLModule.

todo-item/todo-item.module.ts
import { NestjsQueryGraphQLModule } from '@nestjs-query/query-graphql';import { NestjsQueryTypeOrmModule } from '@nestjs-query/query-typeorm';import { Module } from '@nestjs/common';import { TodoItemDTO } from './todo-item.dto';import { TodoItemEntity } from './todo-item.entity';
@Module({  imports: [    NestjsQueryGraphQLModule.forFeature({      imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],      resolvers: [{ DTOClass: TodoItemDTO, EntityClass: TodoItemEntity }],    }),  ],})export class TodoItemModule {}

The generated schema will contain a TodoItem type like the following.

type TodoItem {  id: ID!  title: String!  completed: Boolean!  created: DateTime!  updated: DateTime!  subTasks(    paging: OffsetPaging = { limit: 10 }
    filter: SubTaskFilter = {}
    sorting: [SubTaskSort!] = []  ): TodoItemSubTasksConnection!}

The following mutations will also be automatically exposed.

type Mutation {   addSubTasksToTodoItem(input: RelationsInput!): TodoItem!}
input RelationsInput {  id: ID!  relationIds: [ID!]!}
note

If disableRemove was set to false or not specified a removeSubTasksFromTodoItem mutation would also be exposed with the same arguments as addSubTasksToTodoItem.

Total Count Example#

warning

Enabling totalCount can be expensive. If your table is large the totalCount query may be expensive, use with caution.

info

The totalCount field is not eagerly fetched. It will only be executed if the field is queried from the client.

When using the @OffsetConnection decorator you can enable the totalCount field. The totalCount field will return the total number of records included in the connection.

todo-item/todo-item.dto.ts
import { FilterableField, IDField, OffsetConnection } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';import { SubTaskDTO } from '../sub-task/sub-task.dto'
@ObjectType('TodoItem')@OffsetConnection('subTasks', () => SubTaskDTO, { disableRemove: true, enableTotalCount: true })export class TodoItemDTO {  @IDField(() => ID)  id!: string;
  @FilterableField()  title!: string;
  @FilterableField()  completed!: boolean;
  @FilterableField(() => GraphQLISODateTime)  created!: Date;
  @FilterableField(() => GraphQLISODateTime)  updated!: Date;}

The generated graphql will include a TodoItemSubTasksConnection with a totalCount field.

type TodoItem {  id: ID!  title: String!  completed: Boolean!  created: DateTime!  updated: DateTime!  subTasks(    paging: OffsetPaging = { limit: 10 }
    filter: SubTaskFilter = {}
    sorting: [SubTaskSort!] = []  ): TodoItemSubTasksConnection!}
type TodoItemSubTasksConnection {  pageInfo: OffsetPageInfo!  nodes: [SubTask!]!  totalCount: Int!}

@FilterableOffsetConnection#

The @FilterableOffsetConnection extends the @OffsetConnection decorator exposing the ability to filter the DTO that defines the relation by relation properties.

warning

The @FilterableOffsetConnection decorator will only work with relations defined by the orm used (e.g. typeorm, sequelize). If your relations are federated or you are using mongoose you cannot use the @FilterableConnection decorator.

Example#

In this example we'll use the same Entities defined above to create a graphql endpoint that allows filtering TodoItems by subTasks.

todo-item/todo-item.dto.ts
import { FilterableField, IDField, FilterableOffsetConnection, QueryOptions } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';import { SubTaskDTO } from '../sub-task/sub-task.dto'
@ObjectType('TodoItem')@QueryOptions({ pagingStrategy: PagingStrategies.OFFSET })@FilterableOffsetConnection('subTasks', () => SubTaskDTO, { disableRemove: true })export class TodoItemDTO {  @IDField(() => ID)  id!: string;
  @FilterableField()  title!: string;
  @FilterableField()  completed!: boolean;
  @FilterableField(() => GraphQLISODateTime)  created!: Date;
  @FilterableField(() => GraphQLISODateTime)  updated!: Date;}

Notice the use of @FilterableOffsetConnection instead of @OffsetConnection, by using the @FilterableOffsetConnection version nestjs-query will allow filtering on the subTasks relation.

The module definition remains the same.

todo-item/todo-item.module.ts
import { PagingStrategies } from '@nestjs-query/core';import { NestjsQueryGraphQLModule } from '@nestjs-query/query-graphql';import { NestjsQueryTypeOrmModule } from '@nestjs-query/query-typeorm';import { Module } from '@nestjs/common';import { TodoItemDTO } from './todo-item.dto';import { TodoItemEntity } from './todo-item.entity';
@Module({  imports: [    NestjsQueryGraphQLModule.forFeature({      imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],      resolvers: [{ DTOClass: TodoItemDTO, EntityClass: TodoItemEntity }],    }),  ],})export class TodoItemModule {}

When querying for TodoItems you can now also filter on subTasks properties.

In this example we'll find all todoItems that have subTasks that are completed.

{  todoItems(filter: { subTasks: { completed: { is: true } } }) {    pageInfo {      hasNextPage      hasPreviousPage    }    nodes {      id      title      description      completed      subTasks {        nodes {          title          description          completed        }      }    }  }}

@CursorConnection#

Example#

Based on the entity definitions above we can create a TodoItemDTO with a connection to the subTasks.

todo-item/todo-item.dto.ts
import { FilterableField, IDField, CursorConnection } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';import { SubTaskDTO } from '../sub-task/sub-task.dto'
@ObjectType('TodoItem')@CursorConnection('subTasks', () => SubTaskDTO, { disableRemove: true })export class TodoItemDTO {  @IDField(() => ID)  id!: string;
  @FilterableField()  title!: string;
  @FilterableField()  completed!: boolean;
  @FilterableField(() => GraphQLISODateTime)  created!: Date;
  @FilterableField(() => GraphQLISODateTime)  updated!: Date;}

When specifying a @CursorConnection relation a couple of endpoints will automatically be generated. In this example the following are generated.

  • todoItem.subTasks - A query to retrieve a TodoItems SubTasks.
    • The subTasks property will accept a query to allow you to filter, page and sort results.
    • The subTasks property will return a cursor based connection to page through results.
  • addSubTasksToTodoItem - A mutation to add SubTasks to a TodoItem.
note

In this example we disableRemove because SubTasks cannot exist without a TodoItem.

To set up the resolver you can use the NestjsQueryGraphQLModule.

todo-item/todo-item.module.ts
import { NestjsQueryGraphQLModule } from '@nestjs-query/query-graphql';import { NestjsQueryTypeOrmModule } from '@nestjs-query/query-typeorm';import { Module } from '@nestjs/common';import { TodoItemDTO } from './todo-item.dto';import { TodoItemEntity } from './todo-item.entity';
@Module({  imports: [    NestjsQueryGraphQLModule.forFeature({      imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],      resolvers: [{ DTOClass: TodoItemDTO, EntityClass: TodoItemEntity }],    }),  ],})export class TodoItemModule {}

The generated schema will contain a TodoItem type like the following.

type TodoItem {  id: ID!  title: String!  completed: Boolean!  created: DateTime!  updated: DateTime!  subTasks(    paging: CursorPaging = { first: 10 }
    filter: SubTaskFilter = {}
    sorting: [SubTaskSort!] = []  ): TodoItemSubTasksConnection!}

The following mutations will also be automatically exposed.

type Mutation {   addSubTasksToTodoItem(input: RelationsInput!): TodoItem!}
input RelationsInput {  id: ID!  relationIds: [ID!]!}
note

If disableRemove was set to false or not specified a removeSubTasksFromTodoItem mutation would also be exposed with the same arguments as addSubTasksToTodoItem.

Total Count Example#

warning

Enabling totalCount can be expensive. If your table is large the totalCount query may be expensive, use with caution.

info

The totalCount field is not eagerly fetched. It will only be executed if the field is queried from the client.

When using the @CursorConnection decorator you can enable the totalCount field. The totalCount field will return the total number of records included in the connection.

todo-item/todo-item.dto.ts
import { FilterableField, IDField, Connection } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';import { SubTaskDTO } from '../sub-task/sub-task.dto'
@ObjectType('TodoItem')@CursorConnection('subTasks', () => SubTaskDTO, { disableRemove: true, enableTotalCount: true })export class TodoItemDTO {  @IDField(() => ID)  id!: string;
  @FilterableField()  title!: string;
  @FilterableField()  completed!: boolean;
  @FilterableField(() => GraphQLISODateTime)  created!: Date;
  @FilterableField(() => GraphQLISODateTime)  updated!: Date;}

The generated graphql will include a TodoItemSubTasksConnection with a totalCount field.

type TodoItem {  id: ID!  title: String!  completed: Boolean!  created: DateTime!  updated: DateTime!  subTasks(    paging: CursorPaging = { first: 10 }
    filter: SubTaskFilter = {}
    sorting: [SubTaskSort!] = []  ): TodoItemSubTasksConnection!}
type TodoItemSubTasksConnection {  pageInfo: PageInfo!  edges: [SubTaskEdge!]!  totalCount: Int!}

@FilterableCursorConnection#

The @FilterableCursorConnection extends the @CursorConnection decorator exposing the ability to filter the DTO that defines the relation by relation properties.

warning

The @FilterableCursorConnection decorator will only work with relations defined by the orm used (e.g. typeorm, sequelize). If your relations are federated or you are using mongoose you cannot use the @FilterableConnection decorator.

Example#

In this example we'll use the same Entities defined above to create a graphql endpoint that allows filtering TodoItems by subTasks.

todo-item/todo-item.dto.ts
import { FilterableField, IDField, FilterableCursorConnection } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';import { SubTaskDTO } from '../sub-task/sub-task.dto'
@ObjectType('TodoItem')@FilterableCursorConnection('subTasks', () => SubTaskDTO, { disableRemove: true })export class TodoItemDTO {  @IDField(() => ID)  id!: string;
  @FilterableField()  title!: string;
  @FilterableField()  completed!: boolean;
  @FilterableField(() => GraphQLISODateTime)  created!: Date;
  @FilterableField(() => GraphQLISODateTime)  updated!: Date;}

Notice the use of @FilterableCursorConnection instead of @CursorConnection, by using the @FilterableCursorConnection version nestjs-query will allow filtering on the subTasks relation.

The module definition remains the same.

todo-item/todo-item.module.ts
import { NestjsQueryGraphQLModule } from '@nestjs-query/query-graphql';import { NestjsQueryTypeOrmModule } from '@nestjs-query/query-typeorm';import { Module } from '@nestjs/common';import { TodoItemDTO } from './todo-item.dto';import { TodoItemEntity } from './todo-item.entity';
@Module({  imports: [    NestjsQueryGraphQLModule.forFeature({      imports: [NestjsQueryTypeOrmModule.forFeature([TodoItemEntity])],      resolvers: [{ DTOClass: TodoItemDTO, EntityClass: TodoItemEntity }],    }),  ],})export class TodoItemModule {}

When querying for TodoItems you can now also filter on subTasks properties.

In this example we'll find all todoItems that have subTasks that are completed.

{  todoItems(filter: { subTasks: { completed: { is: true } } }) {    pageInfo {      hasNextPage      hasPreviousPage      startCursor      endCursor    }    edges {      node {        id        title        description        completed        subTasks {          edges {            node {              title              description              completed            }          }        }      }      cursor    }  }}

Virtual Relations#

You may run into a case where you have a virtual relation that does not exist in the database. nestjs-query supports this through the RelationQueryService.

Options#

The following options can be passed to all relation/connection decorators, to customize functionality.

  • relationName - The name of the relation to use when looking up the relation from the QueryService
  • nullable - Set to true if the relation is nullable.
  • complexity - Set to specify relation complexity. For more info see complexity docs
  • disableRead - Set to true to disable read operations.
  • disableUpdate - Set to true to disable update operations.
  • disableRemove - Set to true to disable remove operations.
  • allowFiltering - Set to true to allow filtering on the relation.
  • guards=[] - An array of guards to add to update and remove endpoints.
  • interceptors=[] - An array of interceptors to add to update and remove endpoints.
  • pipes=[] - An array of pipes to add to update and remove endpoints.
  • filters=[] - An array of filters to add to update and remove endpoints.
note

guards, pipes, interceptors and filters will not work by default with relation endpoints. See https://docs.nestjs.com/graphql/tooling#execute-enhancers-at-the-field-resolver-level

Custom Relation Name#

Sometimes you may want to expose a relation that has a different name when persisted from the graphql property. To do this use the relationName property.

// expose todoItem as todoItemRelation in graphql@Relation('todoItemRelation', () => TodoItemDTO, { relationName: 'todoItem' })

Disable Reads#

To disable the read queries you can set the disableRead option to true.

// disable reading the todoItem relation@Relation('todoItem', () => TodoItemDTO, { disableRead: true })

Disable Updates#

To disable the update mutations you can set the disableUpdate option to true.

// disable updates to the relation@Relation('todoItem', () => TodoItemDTO, { disableUpdate: true })

Disable Removes#

To disable the remove mutations you can set the disableRemove option to true.

// disable removing the relation@Relation('todoItem', () => TodoItemDTO, { disableRemove: true })

Guards, Pipes and Filters#

NOTE guards, pipes, interceptors and filters will not work by default with read endpoints. See https://github.com/nestjs/graphql/issues/295

In this example we'll just demonstrate using a guard but the same pattern applies for pipes, filters and interceptors

To set up a guard for endpoint you can use the guards option.

Assume we have the following auth guard that checks for a certain header and value.

auth.guard.ts
import {  CanActivate,  ExecutionContext,  Injectable,  Logger,} from '@nestjs/common';import { Observable } from 'rxjs';import { GqlExecutionContext } from '@nestjs/graphql';import { AUTH_HEADER_NAME } from './constants';import { config } from './config';
@Injectable()export class AuthGuard implements CanActivate {  private logger = new Logger(AuthGuard.name);
  canActivate(    context: ExecutionContext,  ): boolean | Promise<boolean> | Observable<boolean> {    const ctx = GqlExecutionContext.create(context);    const req = ctx.getContext().request;    this.logger.log(`Req = ${req.headers}`);    return req.headers[AUTH_HEADER_NAME] === config.auth.header;  }}

We can then add it to our relations

// Add the AuthGuard using the guards option@Relation('todoItem', () => TodoItemDTO, { guards: [AuthGuard] })

Now any requests that go to the update or remove endpoints will require the guard.

Relation Mixin#

If you are using the resolvers individually you can use the following mixins to add relations functionality.

Relatable#

When using The Relatable mixin adds all relations functionality to a resolver.

In this example we expose on read endpoints for todo items with the relations defined on the TodoItemDTO.

todo-item/todo-item.resolver.ts
import { ReadResolver, Relatable } from '@nestjs-query/query-graphql';import { Resolver } from '@nestjs/graphql';import { AuthGuard } from '../auth.guard';import { SubTaskDTO } from '../sub-task/dto/sub-task.dto';import { TodoItemDTO } from './todo-item.dto';import { TodoItemService } from './todo-item.service';
const guards = [AuthGuard];
@Resolver(() => TodoItemDTO)export class TodoItemResolver extends Relatable(TodoItemDTO)(ReadResolver(TodoItemDTO)) {  constructor(readonly service: TodoItemService) {    super(service);  }}