Relaciones embebidas en MongoDB: Manejo y ejemplos prácticos

Clase 18 de 24Curso de NestJS: Persistencia de Datos con MongoDB

Resumen

MongoDB es una base de datos No Relacional. Aun así, requerimos la posibilidad de crear relaciones entre documentos de diferentes colecciones y es posible hacerlo.

Documento dentro de otro documento

La relación más sencilla y más utilizada es guardando un documento dentro de otro, formando una relación uno a uno embebida.

{
  "name": "Producto Uno",
  "category": {
    "name": "Category",
    "image": "..."
  }
}

Implementación relación uno a uno

Implementar esta lógica con Mongoose y NestJS es muy sencillo.

Paso 1: preparar el esquema

Agrega la propiedad en tu esquema que contendrá el documento embebido.

// products/product.entity.ts
import { Prop, Schema, SchemaFactory, raw } from '@nestjs/mongoose';

export class Product extends Document {

  @Prop(
    raw({
      name: { type: String },
      image: { type: String },
    })
  )
  category: Record<string, any>;
}

El decorador @Prop() recibe un raw() con la estructura del objeto que estará dentro del objeto principal. La relación es resuelta gracias al tipado Record propio de TypeScript.

Paso 2: validar sub documento

El DTO será el encargado de validar la estructura de este sub documento.

// products/dto/category.dto.ts
import { IsString, IsNotEmpty, IsUrl } from 'class-validator';

export class CreateCategoryDto {

  @IsString()
  @IsNotEmpty()
  readonly name: string;
  
  @IsUrl()
  @IsNotEmpty()
  readonly image: string;
}

Prepara el DTO para la creación de la categoría con los campos que le corresponde a la misma que estarán embebidos dentro del documento principal.

// products/products.dto.ts
import { ValidateNested } from 'class-validator';
import { CreateCategoryDto } from './category.dtos';

export class CreateProductDto {

  @IsNotEmpty()
  @ValidateNested()
  @ApiProperty()
  readonly category: CreateCategoryDto;
}

Importa el DTO de la categoría y utilízalo como propiedad para el DTO de creación de productos. Agrégale el decorador @ValidateNested() para que NestJS haga la validación de la estructura correspondiente del objeto dentro.

De esta sencilla manera, puedes crear relaciones uno a una, o guardar un objeto dentro de otro en MongoDB a la vez que válidas la estructura del mismo.

Contribución creada por: Kevin Fiorentino (Platzi Contributor).


Código de ejemplo para relaciones uno a uno embebidas

// src/products/entities/product.entity.ts
import { Prop, Schema, SchemaFactory, raw } from '@nestjs/mongoose';

export class Product extends Document {
  ...

  @Prop(
    raw({
      name: { type: String },
      image: { type: String },
    }),
  )
  category: Record<string, any>; // 👈 new field
}
// src/products/dtos/category.dtos.ts
import { IsString, IsNotEmpty, IsUrl } from 'class-validator';

export class CreateCategoryDto {
  ...

  @IsUrl()
  @IsNotEmpty()
  readonly image: string; // 👈 new field
}
// src/products/dtos/products.dtos.ts

import {
  ValidateNested, // 👈 new decorator
} from 'class-validator';

import { CreateCategoryDto } from './category.dtos';  // 👈

export class CreateProductDto {

  @IsNotEmpty()
  @ValidateNested()
  @ApiProperty()
  readonly category: CreateCategoryDto; // 👈 new field
}