Currently, I have my Entity defined as so:
export class ItemEntity implements Item {
@PrimaryColumn()
@IsIn(['product', 'productVariant', 'category'])
@IsNotEmpty()
itemType: string;
@PrimaryColumn()
@IsUUID()
@IsNotEmpty()
itemId: string;
@OneToOne(() => ProductEntity, product => product.id, { nullable: true })
@JoinColumn()
@Expose({ name: 'bundleItem' })
producttItem: ProductEntity;
@OneToOne(() => ProductVariantEntity, variant => variant.id, {
nullable: true,
})
@JoinColumn()
@Expose({ name: 'bundleItem' })
variantItem: ProductVariantEntity;
@OneToOne(() => CategoryEntity, category => category.id, { nullable: true })
@JoinColumn()
@Expose({ name: 'bundleItem' })
categoryItem: CategoryEntity;
}
I added in the @Expose()
decorator because I want to be able to return one of productItem
, variantItem
, or categoryItem
as just a single bundleItem
field in the response. Any single one of them can have a value, but NOT two or three of them.
But when I do a GET on the ItemEntity's controller, my desired effect is only applied on the first item, not the others:
[
{
"itemType": "category",
"itemId": ""
"bundleItem": {
"categoryType": "Custom",
"description": "First custom category",
"id": "e00ad76c-95d3-4215-84b1-de17c7f1f82c",
"name": "Category A",
"updatedAt": "2023-02-24T08:49:22.913Z"
}
},
{
"itemType": "variant",
"itemId": "",
"bundletem": null
}
]
I want the effect to extend to the other items in the returned array response. But currently, they are null
. Essentially, I want the response to return a bundleItem
field, regardless of what type the itemType
is, be it productItem
, variantItem
, or categoryItem
. Can I achieve that using `class-transformer?
Thanks.
You can probably use the @Transform
for this. The @Transform
decorator contains a few arguments that you can use in the transform process, one of them is the object itself.
@Transform(({ value, key, obj, type }) => value)
The following example contains an object with two properties, if one of them is null
or undefined
we return the other
import {Exclude, Expose, Transform} from "class-transformer";
export class ExampleDto {
constructor(partial: Partial<ExampleDto>) {
Object.assign(this, partial);
}
@Expose()
name: string;
@Expose({ name: "new_property" })
@Transform(({ value, key, obj, type }) => {
return obj.property1 ?? obj.property2;
})
property1: string | null;
@Exclude()
property2: string | null;
}
Controller
@Get("example")
@UseInterceptors(ClassSerializerInterceptor)
getExample(): ExampleDto {
return new ExampleDto({
name: "Name",
property2: "value 2"
});
}
The result is
{
"name": "Name",
"new_property": "value 2"
}
Although this might work, I highly recommend you to not use the same class for your database entity and the DTOs. This can cause all sorts of issues; right now your ItemEntity
is representing two different concepts at the same time, a entity in the database and also a contract with the external world. Any changes in your model would also reflect in the API (and also the other way around).