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
- TodoItemEntity
- SubTaskEntity
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',});import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';import { SchemaTypes, Types, Document } from 'mongoose';
@Schema({ timestamps: true })export class SubTaskEntity extends Document { @Prop({ required: true }) title!: string;
@Prop() description?: string;
@Prop({ required: true }) completed!: boolean;
@Prop({ type: SchemaTypes.ObjectId, ref: 'TodoItemEntity', required: true }) todoItem!: Types.ObjectId;
@Prop() createdAt!: Date;
@Prop() updatedAt!: Date;
@Prop() createdBy?: string;
@Prop() updatedBy?: string;}
export const SubTaskEntitySchema = SchemaFactory.createForClass(SubTaskEntity);
Now that we have the relationship defined we can add the @Relation and @CursorConnection to our DTOs
- TodoItemDTO
- SubTaskDTO
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;}
import { FilterableField, IDField, KeySet, Relation } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';import { TodoItemDTO } from '../../todo-item/dto/todo-item.dto';
@ObjectType('SubTask')@KeySet(['id'])// disable the remove because a sub task cannot exist without a todoitem@Relation('todoItem', () => TodoItemDTO, { disableRemove: true })export class SubTaskDTO { @IDField(() => ID) id!: string;
@FilterableField() title!: string;
@FilterableField({ nullable: true }) description?: string;
@FilterableField() completed!: boolean;
@FilterableField(() => GraphQLISODateTime) createdAt!: Date;
@FilterableField(() => GraphQLISODateTime) updatedAt!: Date;
@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.
- TodoItemEntity
- TagEntity
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);import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';import { Document } from 'mongoose';
@Schema({ timestamps: true })export class TagEntity extends Document { @Prop({ required: true }) name!: string;
@Prop() createdAt!: Date;
@Prop() updatedAt!: Date;
@Prop() createdBy?: string;
@Prop() updatedBy?: string;}
export const TagEntitySchema = SchemaFactory.createForClass(TagEntity);TagEntitySchema.virtual('todoItems', { ref: 'TodoItemEntity', localField: '_id', foreignField: 'tags',});
Now that we have the relationship defined we can add the @CursorConnection to our DTOS
- TodoItemDTO
- TagDTO
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;}import { FilterableField, IDField, KeySet, CursorConnection } from '@nestjs-query/query-graphql';import { ObjectType, ID, GraphQLISODateTime } from '@nestjs/graphql';import { TodoItemDTO } from '../../todo-item/dto/todo-item.dto';
@ObjectType('Tag')@KeySet(['id'])// disable update and remove since it is a virtual in the entity@CursorConnection('todoItems', () => TodoItemDTO, { disableUpdate: true, disableRemove: true })export class TagDTO { @IDField(() => ID) id!: string;
@FilterableField() name!: string;
@FilterableField(() => GraphQLISODateTime) createdAt!: Date;
@FilterableField(() => GraphQLISODateTime) updatedAt!: Date;
@FilterableField({ nullable: true }) createdBy?: string;
@FilterableField({ nullable: true }) updatedBy?: string;}