Hi! I would like to ask your advice about MikroORM integration with class-transformer and class-validator. I have a project with the following dependencies:
And in this project I have a code similar to this:
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { IsArray, MaxLength, ValidateNested, IsOptional } from "class-validator";
import { Type } from "class-transformer";
import { CrudValidationGroups } from "@nestjsx/crud";
import { GroupFilter } from "../group-filter/group-filter.entity";
const { UPDATE, CREATE } = CrudValidationGroups;
@Entity()
export class Group {
@PrimaryGeneratedColumn("uuid")
id: string;
@Column()
@MaxLength(200, { always: true })
@IsOptional({ groups: [UPDATE] })
name: string;
@OneToMany(() => GroupFilter, groupFilter => groupFilter.group, { cascade: ["insert", "update"] })
@Type(() => GroupFilter)
@IsArray({ always: true })
@ValidateNested({ always: true })
@IsOptional({ groups: [UPDATE] })
filters: GroupFilter[];
}
I have the following flow:
I want to replace TypeORM with MikroORM in this project. But MikroORM has collections instead of arrays, so I can't just use class-transformer and class-validator with collections. I think I have three options:
@Transform() and a custom validation using @ValidatorConstraint(). But I can't have transformation/validation options inside @Transform() and @ValidatorConstraint() without hacks.Make some kind of array wrapper for the collection (class-transformer and class-validator can work with Array subclasses):
export class ArrayWrapper<T> extends Array implements Collection<T> {
private readonly collection: Collection<T>;
async loadItems(): Promise<T[]> {
return this.collection.getItems();
}
getItems(): T[] {
return this.collection.getItems();
}
// ...
}
@Entity()
export class Book {
@ManyToMany()
tags = new ArrayWrapper<BookTag>(this);
}
Which option is better? Are there any other options?
I would say none of those 3 you mentioned.
class-transformer creates instances of entities
Don't do this, use em.create() instead. class-transformer will not handle collections properly, and I am quite sure there will be more issues than just this (another one I can imagine is that it will blindly construct entity instances, ignoring identity map, making things less performant). MikroORM handles serialization too (via toJSON() method).
In general, you don't need class-transformer, not for creating entities. You can use class-validator to validate DTOs, but creating entity instances needs to happen via one of the ORM methods (em.create(), em.assign() or em.merge()).
Thank you for the answer! But I have more questions.
em.create()? I am thinking about a setter inside an entity class.em.create() cannot be reused with regular DTOs and the ValidationPipe. But I can implement my own ValidationPipe and use em.create() for entities and class-transformer for regular DTOs. Is there a way to determine if a JS class is an entity? I mean something like this: if (isEntity(maybeEntityClass)) { ... }Array, Set and Map.About performance. I think class-transformer will do the same as a developer would do. Something like this:
const group = new Group();
group.name = name;
for(const groupFilter of groupFilters) {
const groupFilterInstance = new GroupFilter();
groupFilterInstance.prop = groupFilter.prop;
group.filters.add(groupFilterInstance);
}
Utils.isEntity() - it checks a bool flag that is added to the prototype during discovery. it expects entity instance, if you have only class, pass its prototype thereem.create() does tooabout the 1. - you could also use custom types and handle the validation there: https://mikro-orm.io/docs/custom-types
- you will have arrays in the DTO, so you will validate arrays as usual.
I want to use entities as DTOs. In my case, I will have a MikroORM collection after em.create(), not an array.
const entities = em.create(notValidatedUserData);
const errors = await classValidator.validate(entities);
// ...
BTW, is it safe to pass not validated user data to em.create()?
I'm also curious about this. I'd love to just run validate(entity) and know that everything is hunky-dory.