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 '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { TodoItemDTO } from '../todo-item/todo-item.dto.ts';

@ObjectType('SubTask')
@Relation('todoItem', () => TodoItemDTO, { update: { enabled: 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.

To set up the resolver you can use the NestjsQueryGraphQLModule.

sub-task/sub-task.module.ts
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-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 {}

@ptc-org/nestjs-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 remove.enabled was set to true 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 '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { TodoItemDTO } from '../todo-item/todo-item.dto.ts';

@ObjectType('SubTask')
@FilterableRelation('todoItem', () => TodoItemDTO)
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 '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-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 '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { SubTaskDTO } from '../sub-task/sub-task.dto'

@ObjectType('TodoItem')
@UnPagedRelation('subTasks', () => SubTaskDTO, { update: { enabled: 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.

To set up the resolver you can use the NestjsQueryGraphQLModule.

todo-item/todo-item.module.ts
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-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 remove.enabled was set to true 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 '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { SubTaskDTO } from '../sub-task/sub-task.dto'

@ObjectType('TodoItem')
@FilterableUnPagedRelation('subTasks', () => SubTaskDTO, { update: { enabled: 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 '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-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 '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { SubTaskDTO } from '../sub-task/sub-task.dto'

@ObjectType('TodoItem')
@OffsetConnection('subTasks', () => SubTaskDTO, { update: { enabled: 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.

To set up the resolver you can use the NestjsQueryGraphQLModule.

todo-item/todo-item.module.ts
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-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 remove.enabled was set to true 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 '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { SubTaskDTO } from '../sub-task/sub-task.dto'

@ObjectType('TodoItem')
@OffsetConnection('subTasks', () => SubTaskDTO, { update: { enabled: 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 '@ptc-org/nestjs-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, { update: { enabled: 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 '@ptc-org/nestjs-query-core';
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-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 '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { SubTaskDTO } from '../sub-task/sub-task.dto'

@ObjectType('TodoItem')
@CursorConnection('subTasks', () => SubTaskDTO, { update: { enabled: 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.

To set up the resolver you can use the NestjsQueryGraphQLModule.

todo-item/todo-item.module.ts
import { NestjsQueryGraphQLModule } from '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-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 remove.enabled was set to true 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 '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { SubTaskDTO } from '../sub-task/sub-task.dto'

@ObjectType('TodoItem')
@CursorConnection('subTasks', () => SubTaskDTO, { update: { enabled: 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 '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';
import { SubTaskDTO } from '../sub-task/sub-task.dto'

@ObjectType('TodoItem')
@FilterableCursorConnection('subTasks', () => SubTaskDTO, { update: { enabled: 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 '@ptc-org/nestjs-query-graphql';
import { NestjsQueryTypeOrmModule } from '@ptc-org/nestjs-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.
  • update
    • enabled - Set to true to enable update operations.
    • description - The description of the update operation.
    • complexity - Set to specify relation complexity. For more info see complexity docs
    • decorators=[] - An array of custom PropertyDecorator or MethodDecorators to add to the endpoint.
  • remove
    • enabled - Set to true to enable remove operations.
    • description - The description of the remove operation.
    • complexity - Set to specify relation complexity. For more info see complexity docs
    • decorators=[] - An array of custom PropertyDecorator or MethodDecorators to add to the endpoint.
  • 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' })

GraphQL lookahead

caution

This functionality only works for typeorm!

When you want to optimze your database queries you can set enableLookAhead, this will join and select the relation when it's being fetched in the query.

// enable enableLookAhead for the todoItem relation
@Relation('todoItemRelation', () => TodoItemDTO, { enableLookAhead: true })

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 filter or sorting in relations

To disable the filter or sorting of relations you can set the disableFilter or/and disableSort option to true.

This is not available in relation as it will only fetch one record.

Enable Updates

To enable the update mutations you can set the update.enabled option to true.

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

Enable Removes

To enable the remove mutations you can set the remove.enabled option to true.

// disable removing the relation
@Relation('todoItem', () => TodoItemDTO, { update: { enabled: 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 '@ptc-org/nestjs-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);
}
}