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
.
@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
- 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.
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
- 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
.
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
- 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
.
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
- 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
.
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 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.update
enabled
- Set totrue
to 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 customPropertyDecorator
orMethodDecorators
to add to the endpoint.
remove
enabled
- Set totrue
to 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 customPropertyDecorator
orMethodDecorators
to add to the endpoint.
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.
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);
}
}