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 relationDTO
.@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 relationDTO
.@OffsetConnection
- A connection that represents a collection (e.g, many-to-many, one-to-many) that usesoffset
based pagination.@FilterableOffsetConnection
- An@OffsetConnection
that 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 usescursor
based pagination.@FilterableCursorConnection
- A@CursorConnection
that enables filtering the parent by fields of the connectionDTO
.
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.
- 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;}
#
@RelationA relation that is a single value (one-to-one, many-to-one)
#
ExampleBased 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 '@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
- Aquery
to retrieve theSubTasks
TodoItem
setTodoItemOnSubTask
- Amutation
to set theTodoItem
on aSubTask
.removeTodoItemFromSubTask
- Amutation
to remove aTodoItem
/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
.
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
.
#
@FilterableRelationThe @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.
#
ExampleIn 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 '@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.
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 }}
#
@UnPagedRelationYou can also use the @UnPagedRelation
decorator to define a relation that does not use paging and returns an array
of all the related records.
#
ExampleBased on the entity definition above we can define a TodoItemDTO
with a subTasks
relation.
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
- Aquery
endpoint to retrieve aTodoItems
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.
- The
addSubTasksToTodoItem
- Amutation
to addSubTasks
to aTodoItem
.
note
In this example we disableRemove
because SubTasks
cannot exist without a TodoItem
.
To set up the resolver you can use the NestjsQueryGraphQLModule
.
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
.
#
@FilterableUnPagedRelationThe @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.
#
ExampleIn 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 '@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.
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#
ExampleBased on the entity definitions above we can create a TodoItemDTO
with a connection to the subTasks
.
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
- Aquery
to retrieve aTodoItems
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.
- The
addSubTasksToTodoItem
- Amutation
to addSubTasks
to aTodoItem
.
note
In this example we disableRemove
because SubTasks
cannot exist without a TodoItem
.
To set up the resolver you can use the NestjsQueryGraphQLModule
.
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 Examplewarning
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.
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!}
#
@FilterableOffsetConnectionThe @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.
#
ExampleIn 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 '@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.
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#
ExampleBased on the entity definitions above we can create a TodoItemDTO
with a connection to the subTasks
.
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
- Aquery
to retrieve aTodoItems
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.
- The
addSubTasksToTodoItem
- Amutation
to addSubTasks
to aTodoItem
.
note
In this example we disableRemove
because SubTasks
cannot exist without a TodoItem
.
To set up the resolver you can use the NestjsQueryGraphQLModule
.
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 Examplewarning
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.
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!}
#
@FilterableCursorConnectionThe @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.
#
ExampleIn 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 '@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.
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 RelationsYou 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.
#
OptionsThe 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 theQueryService
nullable
- Set totrue
if the relation is nullable.complexity
- Set to specify relation complexity. For more info see complexity docsdisableRead
- Set totrue
to disable read operations.disableUpdate
- Set totrue
to disable update operations.disableRemove
- Set totrue
to disable remove operations.allowFiltering
- Set totrue
to allow filtering on the relation.guards=[]
- An array of guards to add toupdate
andremove
endpoints.interceptors=[]
- An array of interceptors to add toupdate
andremove
endpoints.pipes=[]
- An array of pipes to add toupdate
andremove
endpoints.filters=[]
- An array of filters to add toupdate
andremove
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 NameSometimes 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' })
#
Disable ReadsTo 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 UpdatesTo disable the update
mutations
you can set the disableUpdate
option to true
.
- Relation
- CursorConnection
- OffsetConnection
- UnPagedRelation
// disable updates to the relation@Relation('todoItem', () => TodoItemDTO, { disableUpdate: true })
// disable updating subTasks@CursorConnection('subTasks', () => SubTaskDTO, { disableUpdate: true })
// disable updating subTasks@OffsetConnection('subTasks', () => SubTaskDTO, { disableUpdate: true })
// disable updating subTasks@UnPagedRelation('subTasks', () => SubTaskDTO, { disableUpdate: true })
#
Disable RemovesTo disable the remove
mutations
you can set the disableRemove
option to true
.
- Relation
- CursorConnection
- OffsetConnection
- UnPagedRelation
// disable removing the relation@Relation('todoItem', () => TodoItemDTO, { disableRemove: true })
// disable removing subTasks from the connection@CursorConnection('subTasks', () => SubTaskDTO, { disableRemove: true })
// disable removing subTasks from the connection@OffsetConnection('subTasks', () => SubTaskDTO, { disableRemove: true })
// disable removing subTasks from the relations@UnPagedRelation('subTasks', () => SubTaskDTO, { disableRemove: true })
#
Guards, Pipes and FiltersNOTE 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 MixinIf you are using the resolvers individually you can use the following mixins to add relations functionality.
#
RelatableWhen 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 '@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); }}