Skip to main content

Using with class-transformer

Last updated for:

@typegoose/typegoose@10.0.0
class-transformer@0.5.1

This guide shows how to use typegoose with class-transformer.

npm install --save class-transformer@~0.5.1

Implementation

Suppose you have this Account class decorated with class-transformer:

import { Exclude, Expose, Transform } from 'class-transformer';
import { getModelForClass, mongoose, prop } from '@typegoose/typegoose';

// re-implement base Document to allow class-transformer to serialize/deserialize its properties
// This class is needed, otherwise "_id" and "__v" would be excluded from the output
class DocumentCT {
@Expose()
// makes sure that when deserializing from a Mongoose Object, ObjectId is serialized into a string
@Transform((value) => {
if ('value' in value) {
// HACK: this is changed because of https://github.com/typestack/class-transformer/issues/879
// return value.value.toString(); // because "toString" is also a wrapper for "toHexString"
return value.obj[value.key].toString();
}

return 'unknown value';
})
public _id: string;

@Expose()
public __v: number;
}

@Exclude()
class Account extends DocumentCT {
@prop()
@Expose()
public email: string;

@prop()
@Expose({ groups: ['admin'] })
public password: string;
}

const AccountModel = getModelForClass(Account);

Side-Note: Typegoose doesn't provide a class like DocumentCT by default, because this would require adding class-transformer as a dependency.

You can then use, for example:

  • lean() on the query:

    // lean return a Plain Old Javascript Object
    const pojo = await AccountModel.findById(id).orFail().lean().exec();
    // deserialize Plain Old Javascript Object into an instance of the Account class
    const deserialized = plainToClass(Account, pojo);
    // serialize Account instance back to a Plain Old Javascript Object, applying class-transformer's magic
    const serialized = instanceToPlain(deserialized);
  • or a normal document:

    // exec returns a Mongoose Object
    const mo = await AccountModel.findById(id).orFail().exec();
    // deserialize Mongoose Object into an instance of the Account class
    const deserialized = plainToClass(Account, mo);
    // serialize Account instance back to a Plain Old Javascript Object, applying class-transformer's magic
    const serialized = instanceToPlain(deserialized);

As you can see from these examples, there is:

  • a redundant step to first turn the output of the query into a full instance of Account : plainToClass(..., ...)
  • before being able to benefit from its features for serialization: instanceToPlain(...)

The reason for doing this is so queries will output DocumentType<Account> (Mongoose Document) instead of required Account (Plain Object / instance of the Class) in this example.

class-transformer can only operate its magic on instances of annotated classes.


info

For more information, you can always look at the typegoose class-transformer tests