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
- Yarn
- pnpm
- Bun
npm install --save class-transformer@~0.5.1
yarn add class-transformer@~0.5.1
pnpm add class-transformer@~0.5.1
bun add 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