This guide helps you migrate from class-transformer to om-data-mapper for better performance while maintaining compatibility.
The easiest way to migrate is using the compatibility layer:
// Before (class-transformer)
import { plainToClass, Expose, Type } from 'class-transformer';
// After (om-data-mapper) - Just change the import!
import { plainToClass, Expose, Type } from 'om-data-mapper/class-transformer-compat';
// Your existing code works exactly the same, but up to 42.7x faster! 🚀Before (class-transformer):
import 'reflect-metadata';
import { plainToClass, Expose } from 'class-transformer';
class UserDTO {
@Expose()
id: number;
@Expose({ name: 'user_name' })
name: string;
}
const user = plainToClass(UserDTO, { id: 1, user_name: 'John' });After (om-data-mapper - Compatibility Layer):
// No reflect-metadata needed!
import { plainToClass, Expose } from 'om-data-mapper/class-transformer-compat';
class UserDTO {
@Expose()
id: number;
@Expose({ name: 'user_name' })
name: string;
}
const user = plainToClass(UserDTO, { id: 1, user_name: 'John' });After (om-data-mapper - Native API, Recommended):
import { Mapper, Map, plainToInstance } from 'om-data-mapper';
@Mapper<UserSource, UserDTO>()
class UserMapper {
@Map('id')
id!: number;
@Map('user_name')
name!: string;
}
const user = plainToInstance(UserMapper, { id: 1, user_name: 'John' });Before (class-transformer):
import { plainToClass, Type } from 'class-transformer';
class AddressDTO {
street: string;
city: string;
}
class UserDTO {
name: string;
@Type(() => AddressDTO)
address: AddressDTO;
}
const user = plainToClass(UserDTO, data);After (om-data-mapper - Compatibility Layer):
import { plainToClass, Type } from 'om-data-mapper/class-transformer-compat';
class AddressDTO {
street: string;
city: string;
}
class UserDTO {
name: string;
@Type(() => AddressDTO)
address: AddressDTO;
}
const user = plainToClass(UserDTO, data);After (om-data-mapper - Native API, Recommended):
import { Mapper, Map, MapWith, plainToInstance } from 'om-data-mapper';
@Mapper<AddressSource, AddressDTO>()
class AddressMapper {
@Map('street')
street!: string;
@Map('city')
city!: string;
}
@Mapper<UserSource, UserDTO>()
class UserMapper {
@Map('name')
name!: string;
@MapWith(AddressMapper)
@Map('address')
address!: AddressDTO;
}
const user = plainToInstance(UserMapper, data);Before (class-transformer):
import { plainToClass, Transform } from 'class-transformer';
class UserDTO {
@Transform(({ value }) => value.toUpperCase())
name: string;
@Transform(({ value }) => new Date(value))
createdAt: Date;
}
const user = plainToClass(UserDTO, data);After (om-data-mapper - Compatibility Layer):
import { plainToClass, Transform } from 'om-data-mapper/class-transformer-compat';
class UserDTO {
@Transform(({ value }) => value.toUpperCase())
name: string;
@Transform(({ value }) => new Date(value))
createdAt: Date;
}
const user = plainToClass(UserDTO, data);After (om-data-mapper - Native API, Recommended):
import { Mapper, MapFrom, Transform, plainToInstance } from 'om-data-mapper';
@Mapper<UserSource, UserDTO>()
class UserMapper {
@MapFrom((src: UserSource) => src.name.toUpperCase())
name!: string;
@MapFrom((src: UserSource) => new Date(src.createdAt))
createdAt!: Date;
}
const user = plainToInstance(UserMapper, data);Before (class-transformer):
import { plainToClass } from 'class-transformer';
const users = data.map(item => plainToClass(UserDTO, item));After (om-data-mapper - Compatibility Layer):
import { plainToClass } from 'om-data-mapper/class-transformer-compat';
const users = data.map(item => plainToClass(UserDTO, item));After (om-data-mapper - Native API, Recommended):
import { plainToInstanceArray } from 'om-data-mapper';
// More efficient - single mapper instance
const users = plainToInstanceArray(UserMapper, data);Before (class-transformer):
import { Exclude } from 'class-transformer';
class UserDTO {
name: string;
@Exclude()
password: string;
}After (om-data-mapper - Compatibility Layer):
import { Exclude } from 'om-data-mapper/class-transformer-compat';
class UserDTO {
name: string;
@Exclude()
password: string;
}After (om-data-mapper - Native API, Recommended):
import { Mapper, Map, Ignore } from 'om-data-mapper';
@Mapper<UserSource, UserDTO>()
class UserMapper {
@Map('name')
name!: string;
@Ignore()
password!: string;
}| Feature | class-transformer | om-data-mapper |
|---|---|---|
| Metadata | Requires reflect-metadata |
No metadata needed |
| Decorators | Legacy experimental | TC39 Stage 3 (standard) |
| Performance | Baseline | Up to 42.7x faster |
| Dependencies | Has dependencies | Zero dependencies |
| Bundle Size | Larger | Smaller (tree-shakeable) |
| Type Safety | Limited | Full TypeScript support |
- Remove
import 'reflect-metadata'from your code - Update
tsconfig.jsonto use TC39 decorators (see TypeScript Configuration) - Change imports from
class-transformertoom-data-mapper/class-transformer-compat - Test your transformations
- (Optional) Migrate to native API for better performance and type safety
Update your tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"experimentalDecorators": false, // Do not use legacy decorators
"emitDecoratorMetadata": false // Not needed
}
}-
Reuse mapper instances instead of creating new ones:
// ❌ Slow data.map(item => plainToClass(UserDTO, item)); // ✅ Fast const mapper = getMapper(UserMapper); data.map(item => mapper.transform(item));
-
Use batch functions for arrays:
// ✅ More efficient plainToInstanceArray(UserMapper, data);
-
Enable unsafe mode for trusted data:
@Mapper<Source, Target>({ unsafe: true }) class FastMapper { /* ... */ }
- Documentation: README.md
- API Reference: TypeDoc
- Issues: GitHub Issues
- Discussions: GitHub Discussions