Relations
Relations work a little differently in typegoose
when compared to other relational ORMs such as sequelize
or
typeorm
. You can read more about relations (references) in typegoose
[here](https://mongoosejs.com/docs/populate
.html)
There are multiple ways to set of references in Typegoose. These are intended as a starting point.
Filtering on references is not supported by Typegoose.
One to Many/Many To One Example
To set up a one to many/many to one relationship in Typegoose, 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
and an array of sub-tasks on our todoItem
entity.
- TodoItemEntity
- SubTaskEntity
import { Prop, modelOptions } from '@typegoose/typegoose';
import { Base } from '@typegoose/typegoose/lib/defaultClasses';
@modelOptions({ schemaOptions: { timestamps: true } })
export class TodoItemEntity extends Base {
@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({ ref: () => SubTaskEntity })
subTasks: Ref<SubTaskEntity>[];
@Prop()
createdBy?: string;
@Prop()
updatedBy?: string;
}
import { Prop, modelOptions, Ref } from '@typegoose/typegoose';
import { Base } from '@typegoose/typegoose/lib/defaultClasses';
@modelOptions({ schemaOptions: { timestamps: true } })
export class SubTaskEntity extends Base {
@Prop({ required: true })
title!: string;
@Prop()
description?: string;
@Prop({ required: true })
completed!: boolean;
@Prop({ ref: () => TodoItemEntity, required: true })
todoItem!: Ref<TodoItemEntity>;
@Prop()
createdAt!: Date;
@Prop()
updatedAt!: Date;
@Prop()
createdBy?: string;
@Prop()
updatedBy?: string;
}
Now that we have the relationships defined, we can add the @Relation
and @Connection
to our DTOs
- TodoItemDTO
- SubTaskDTO
import { FilterableField, IDField, KeySet, Connection } from '@ptc-org/nestjs-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
@Connection('subTasks', () => SubTaskDTO, { update: { enabled: 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 '@ptc-org/nestjs-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, { update: { enabled: 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, modelOption, Ref } from '@typegoose/typegoose';
import { Base } from '@typegoose/typegoose/lib/defaultClasses';
@modelOptions({ schemaOptions: { timestamps: true } })
export class TodoItemEntity extends Base {
@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({ ref: () => TagEntity })
tags!: Ref<TagEntity>[];
@Prop({ default: 0 })
priority!: number;
@Prop()
createdBy?: string;
@Prop()
updatedBy?: string;
public get id(): string {
// eslint-disable-next-line no-underscore-dangle
return this._id.toHexString();
}
}
import { Base } from '@typegoose/typegoose/lib/defaultClasses';
import { Prop, modelOptions, Ref } from '@typegoose/typegoose';
import { Types } from 'mongoose';
import { TodoItemEntity } from '../todo-item/todo-item.entity';
@modelOptions({
schemaOptions: {
timestamps: true,
collection: 'tags',
toObject: { virtuals: true },
},
})
export class TagEntity implements Base {
_id!: Types.ObjectId;
id!: string;
@Prop({ required: true })
name!: string;
@Prop()
createdAt!: Date;
@Prop()
updatedAt!: Date;
@Prop()
createdBy?: string;
@Prop()
updatedBy?: string;
@Prop({
ref: 'TodoItemEntity',
localField: '_id',
foreignField: 'tags',
})
todoItems?: Ref<TodoItemEntity>[];
}
Now that we have the relationship defined, we can add the @Connection
to our DTOS
- TodoItemDTO
- TagDTO
import { FilterableField, IDField, KeySet, Connection } from '@ptc-org/nestjs-query-graphql';
import { ObjectType, ID, GraphQLISODateTime, Field } from '@nestjs/graphql';
import { TagDTO } from '../../tag/dto/tag.dto';
@ObjectType('TodoItem')
@KeySet(['id'])
@Connection('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, Connection } from '@ptc-org/nestjs-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
@Connection('todoItems', () => TodoItemDTO)
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;
}