Skip to main content

Relations

Relations work a little differently in mongoose when compared to other relational ORMs such as sequelize or typescript. You can read more about relations (references) in mongoose here

note

There are multiple ways to set of references in mongoose there are intended as starting point.

warning

Filtering on references is not supported by mongoose.

One to Many/Many To One Example#

To set up a one to many/many to one relationship in mongoose you will store a reference in your document

For example lets add sub tasks to our todo items by storing a todoItem ref on our subTask

todo-item/todo-item.entity.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';import { SchemaTypes, Types, Document } from 'mongoose';
@Schema({ timestamps: true })export class TodoItemEntity extends Document {  @Prop({ required: true })  title!: string;
  @Prop()  description?: string;
  @Prop({ required: true })  completed!: boolean;
  @Prop({ default: Date.now })  createdAt!: Date;
  @Prop({ default: Date.now })  updatedAt!: Date;
  @Prop({ default: 0 })  priority!: number;
  @Prop()  createdBy?: string;
  @Prop()  updatedBy?: string;}
export const TodoItemEntitySchema = SchemaFactory.createForClass(TodoItemEntity);TodoItemEntitySchema.virtual('subTasks', {  ref: 'SubTaskEntity',  localField: '_id',  foreignField: 'todoItem',});

Now that we have the relationship defined we can add the @Relation and @CursorConnection to our DTOs

todo-item/todo-item.dto.ts
import { FilterableField, IDField, KeySet, CursorConnection } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';import { SubTaskDTO } from '../../sub-task/dto/sub-task.dto';
@ObjectType('TodoItem')@KeySet(['id'])// disable the remove because mongoose does not support removing a virtual@CursorConnection('subTasks', () => SubTaskDTO, { disableRemove: true })export class TodoItemDTO {  @IDField(() => ID)  id!: string;
  @FilterableField()  title!: string;
  @FilterableField({ nullable: true })  description?: string;
  @FilterableField()  completed!: boolean;
  @FilterableField(() => GraphQLISODateTime)  createdAt!: Date;
  @FilterableField(() => GraphQLISODateTime)  updatedAt!: Date;
  @Field()  age!: number;
  @FilterableField()  priority!: number;
  @FilterableField({ nullable: true })  createdBy?: string;
  @FilterableField({ nullable: true })  updatedBy?: string;}

Many To Many Example#

In this example we'll add tags to todoItems by storing an array of tag references on the todoItems.

todo-item/todo-item.entity.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';import { SchemaTypes, Types, Document } from 'mongoose';
@Schema({ timestamps: true })export class TodoItemEntity extends Document {  @Prop({ required: true })  title!: string;
  @Prop()  description?: string;
  @Prop({ required: true })  completed!: boolean;
  @Prop({ default: Date.now })  createdAt!: Date;
  @Prop({ default: Date.now })  updatedAt!: Date;
  // notice the brackets around the prop options  @Prop([{ type: SchemaTypes.ObjectId, ref: 'TagEntity' }])  tags!: Types.ObjectId[];
  @Prop({ default: 0 })  priority!: number;
  @Prop()  createdBy?: string;
  @Prop()  updatedBy?: string;}
export const TodoItemEntitySchema = SchemaFactory.createForClass(TodoItemEntity);

Now that we have the relationship defined we can add the @CursorConnection to our DTOS

todo-item/todo-item.dto.ts
import { FilterableField, IDField, KeySet, CursorConnection } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';import { TagDTO } from '../../tag/dto/tag.dto';
@ObjectType('TodoItem')@KeySet(['id'])@CursorConnection('tags', () => TagDTO)export class TodoItemDTO {  @IDField(() => ID)  id!: string;
  @FilterableField()  title!: string;
  @FilterableField({ nullable: true })  description?: string;
  @FilterableField()  completed!: boolean;
  @FilterableField(() => GraphQLISODateTime)  createdAt!: Date;
  @FilterableField(() => GraphQLISODateTime)  updatedAt!: Date;
  @Field()  age!: number;
  @FilterableField()  priority!: number;
  @FilterableField({ nullable: true })  createdBy?: string;
  @FilterableField({ nullable: true })  updatedBy?: string;}