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@Relationthat enables filtering the parent by fields of the relationDTO.@UnPagedRelation- An array of relations (e.g, many-to-many, one-to-many) that returns all of the related records.@FilterableUnPagedRelation- An@UnPagedRelationthat enables filtering the parent by fields of the relationDTO.@OffsetConnection- A connection that represents a collection (e.g, many-to-many, one-to-many) that usesoffsetbased pagination.@FilterableOffsetConnection- An@OffsetConnectionthat enables filtering the parent by fields of the connectionDTO.@CursorConnection- A connection that represents a collection (e.g, many-to-many, one-to-many) that usescursorbased pagination.@FilterableCursorConnection- A@CursorConnectionthat enables filtering the parent by fields of the connectionDTO.
@FilterableUnPagedRelation, @FilterableOffsetConnection, and @FilterableCursorConnection are not supported by
mongoose!
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.
- TodoItemEntity
- SubTaskEntity
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;
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
ObjectType,
ManyToOne,
JoinColumn,
} from 'typeorm';
import { TodoItemEntity } from '../todo-item/todo-item.entity';
@Entity()
export class SubTaskEntity {
@PrimaryGeneratedColumn()
id!: string;
@Column()
title!: string;
@Column()
completed!: boolean;
@Column({ nullable: false })
todoItemId!: string;
@ManyToOne(
(): ObjectType<TodoItemEntity> => TodoItemEntity,
td => td.subTasks,
{ onDelete: 'CASCADE', nullable: false },
)
@JoinColumn()
todoItem!: TodoItemEntity;
@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.
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- Aqueryto retrieve theSubTasksTodoItemsetTodoItemOnSubTask- Amutationto set theTodoItemon aSubTask.removeTodoItemFromSubTask- Amutationto remove aTodoItem/SubTaskrelation.- NOTE This does not typically remove either record just removes the relation.
To set up the resolver you can use the NestjsQueryGraphQLModule.
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!
}
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.
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.
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.
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.
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- Aqueryendpoint to retrieve aTodoItemsSubTasks.- The
subTasksproperty will accept a query to allow you to filter, and sort results. - The
subTaskswill be returned as an array of results.
- The
addSubTasksToTodoItem- Amutationto addSubTasksto aTodoItem.
To set up the resolver you can use the NestjsQueryGraphQLModule.
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!]!
}
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.
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.
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.
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.
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- Aqueryto retrieve aTodoItemsSubTasks.- The
subTasksproperty will accept a query to allow you to filter, page and sort results. - The
subTasksproperty will return a offset based connection to page through results.
- The
addSubTasksToTodoItem- Amutationto addSubTasksto aTodoItem.
To set up the resolver you can use the NestjsQueryGraphQLModule.
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!]!
}
If remove.enabled was set to true a removeSubTasksFromTodoItem mutation would also be exposed with the same arguments as addSubTasksToTodoItem.
Total Count Example
Enabling totalCount can be expensive. If your table is large the totalCount query may be expensive, use with caution.
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.
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.
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.
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.
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.
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- Aqueryto retrieve aTodoItemsSubTasks.- The
subTasksproperty will accept a query to allow you to filter, page and sort results. - The
subTasksproperty will return a cursor based connection to page through results.
- The
addSubTasksToTodoItem- Amutationto addSubTasksto aTodoItem.
To set up the resolver you can use the NestjsQueryGraphQLModule.
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!]!
}
If remove.enabled was set to true a removeSubTasksFromTodoItem mutation would also be exposed with the same arguments as addSubTasksToTodoItem.
Total Count Example
Enabling totalCount can be expensive. If your table is large the totalCount query may be expensive, use with caution.
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.
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.
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.
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.
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 theQueryServicenullable- Set totrueif the relation is nullable.complexity- Set to specify relation complexity. For more info see complexity docsdisableRead- Set totrueto disable read operations.updateenabled- Set totrueto enable update operations.description- The description of the update operation.complexity- Set to specify relation complexity. For more info see complexity docsdecorators=[]- An array of customPropertyDecoratororMethodDecoratorsto add to the endpoint.
removeenabled- Set totrueto enable remove operations.description- The description of the remove operation.complexity- Set to specify relation complexity. For more info see complexity docsdecorators=[]- An array of customPropertyDecoratororMethodDecoratorsto add to the endpoint.
allowFiltering- Set totrueto allow filtering on the relation.guards=[]- An array of guards to add toupdateandremoveendpoints.interceptors=[]- An array of interceptors to add toupdateandremoveendpoints.pipes=[]- An array of pipes to add toupdateandremoveendpoints.filters=[]- An array of filters to add toupdateandremoveendpoints.
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.
- Relation
- CursorConnection
- OffsetConnection
- UnPagedRelation
// expose todoItem as todoItemRelation in graphql
@Relation('todoItemRelation', () => TodoItemDTO, { relationName: 'todoItem' })
// expose subTasks as subTaskConnection in graphql
@CursorConnection('subTaskConnection', () => SubTaskDTO, { relationName: 'subTasks' })
// expose subTasks as subTaskConnection in graphql
@OffsetConnection('subTaskConnection', () => SubTaskDTO, { relationName: 'subTasks' })
// expose subTasks as subTaskConnection in graphql
@UnPagedRelation('subTasks', () => SubTaskDTO, { relationName: 'subTasks' })
GraphQL lookahead
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.
- Relation
// 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.
- Relation
- CursorConnection
- OffsetConnection
- UnPagedRelation
// disable reading the todoItem relation
@Relation('todoItem', () => TodoItemDTO, { disableRead: true })
// disable reading the connection
@CursorConnection('subTasks', () => SubTaskDTO, { disableRead: true })
// disable reading the relation
@OffsetConnection('subTaskConnection', () => SubTaskDTO, { disableRead: true })
// disable reading the relation
@UnPagedRelation('subTaskConnection', () => SubTaskDTO, { 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.
- Relation
- CursorConnection
- OffsetConnection
- UnPagedRelation
This is not available in relation as it will only fetch one record.
// disable reading the connection
@CursorConnection('subTasks', () => SubTaskDTO, { disableFilter: true, disableSort: true })
// disable reading the relation
@OffsetConnection('subTaskConnection', () => SubTaskDTO, { disableFilter: true, disableSort: true })
// disable reading the relation
@UnPagedRelation('subTaskConnection', () => SubTaskDTO, { disableFilter: true, disableSort: true })
Enable Updates
To enable the update mutations you can set the update.enabled option to true.
- Relation
- CursorConnection
- OffsetConnection
- UnPagedRelation
// disable updates to the relation
@Relation('todoItem', () => TodoItemDTO, { update: { enabled: true } })
// disable updating subTasks
@CursorConnection('subTasks', () => SubTaskDTO, { update: { enabled: true } })
// disable updating subTasks
@OffsetConnection('subTasks', () => SubTaskDTO, { update: { enabled: true } })
// disable updating subTasks
@UnPagedRelation('subTasks', () => SubTaskDTO, { update: { enabled: true } })
Enable Removes
To enable the remove mutations you can set the remove.enabled option to true.
- Relation
- CursorConnection
- OffsetConnection
- UnPagedRelation
// disable removing the relation
@Relation('todoItem', () => TodoItemDTO, { update: { enabled: true } })
// disable removing subTasks from the connection
@CursorConnection('subTasks', () => SubTaskDTO, { update: { enabled: true } })
// disable removing subTasks from the connection
@OffsetConnection('subTasks', () => SubTaskDTO, { update: { enabled: true } })
// disable removing subTasks from the relations
@UnPagedRelation('subTasks', () => SubTaskDTO, { 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.
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
- Relation
- CursorConnection
- OffsetConnection
- UnPagedRelation
// Add the AuthGuard using the guards option
@Relation('todoItem', () => TodoItemDTO, { guards: [AuthGuard] })
// Add the AuthGuard using the guards option
@CursorConnection('subTasks', () => SubTaskDTO, { guards: [AuthGuard] })
// Add the AuthGuard using the guards option
@OffsetConnection('subTasks', () => SubTaskDTO, { guards: [AuthGuard] })
// Add the AuthGuard using the guards option
@UnPagedRelation('subTasks', () => SubTaskDTO, { 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.
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);
}
}