Digamos que tengo una colección de events simple creada por TypeGraphql y Typegoose que almacena objetos como:
{ _id: ObjectId(...), name: 'SomeEvent', category: ObjectId('...') }
y tipo correspondiente:
@ObjectType() export class Event { @Field(() => ID) _id!: Types.ObjectId @prop({ ref: 'Category' }) @Field(() => Category) category!: Ref<Category> @prop() @Field() name!: string } También tengo categories de colección que ahora solo contienen _id y name .
Ahora quiero insertar algún evento en la base de datos. ¿Es posible verificar automáticamente si el ID de categoría proporcionado en la entrada existe en las categories de la colección y, si no es así, arroja un error? En este momento, el evento se puede agregar con cualquier cosa en el campo de category y luego, cuando intento obtenerlo mediante una consulta, arroja un error que indica que la category no se puede resolver porque no hay una categoría con esta ID. Sé que puedo verificarlo manualmente durante la adición de eventos, pero si tengo más campos como ese, será problemático.
Con la ayuda de la respuesta de Martin Devillers, pude escribir un validador para validar los documentos de referencia usando el validador de clase con typegoose.
Este es mi refdoc.validator.ts:
import { ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface } from "class-validator"; import { Injectable } from "@nestjs/common"; import { getModelForClass } from "@typegoose/typegoose"; @ValidatorConstraint({ name: "RefDoc", async: true }) @Injectable() export class RefDocValidator implements ValidatorConstraintInterface { async validate(refId: string, args: ValidationArguments) { const modelClass = args.constraints[0]; return getModelForClass(modelClass).exists({ _id: refId }) } defaultMessage(): string { return "Referenced Document not found!"; } }Luego puedo aplicarlo en el DTO o modelo con @Validate-Decorator. El argumento que estoy pasando es el modelo de ganso tipográfico.
@Validate(RefDocValidator, [Costcenter]) costcenterId: string;Parece estar funcionando para mí, estoy abierto a cualquier mejora.
Editar: incluso mejor con un decorador personalizado, como sugirió Martin Devillers:
refdoc.validator.ts
import { registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface } from "class-validator"; import { Injectable } from "@nestjs/common"; import { getModelForClass } from "@typegoose/typegoose"; @ValidatorConstraint({ name: "RefDoc", async: true }) @Injectable() export class RefDocValidator implements ValidatorConstraintInterface { async validate(refId: string, args: ValidationArguments) { const modelClass = args.constraints[0]; return getModelForClass(modelClass).exists({ _id: refId }) } defaultMessage(): string { return "Referenced Document not found!"; } } export function RefDocExists(modelClass: any, validationOptions?: ValidationOptions) { return function (object: Object, propertyName: string) { registerDecorator({ name: 'RefDocExists', target: object.constructor, propertyName: propertyName, constraints: [modelClass], options: validationOptions, validator: RefDocValidator, }); }; }Entonces puedes usarlo en el DTO como:
@ApiProperty() @IsNotEmpty() //@Validate(RefDocValidator, [Costcenter]) old @RefDocExists(Costcenter) //new costcenterId: string;Fuera de la caja, ni MongoDB ni mongoose ni typegoose ofrecen comprobaciones automatizadas de integridad referencial .
A nivel de base de datos, esta función no existe (que también es una de las diferencias fundamentales entre una base de datos como MongoDB y SQL Server/Oracle).
Sin embargo, a nivel de aplicación, puede lograr este comportamiento de varias maneras:
EventModel en varios lugares de su base de código y no quiere repetir su lógica de validación. Por ejemplo: EventModel.path('category').validate(async (value, respond) => { const categoryExists = await CategoryModel.exists({ _id: value }) respond(categoryExists) })const categoryExists = await CategoryModel.exists({ _id: event.category })Para reiterar: todas las opciones anteriores son provisionales. Siempre es posible que alguien vaya directamente a su base de datos y rompa la integridad referencial de todos modos.