Usage with NestJS
@automapper/nestjs
is the official integration for NestJS.
Installation
npm i @automapper/core @automapper/nestjs
yarn add @automapper/core @automapper/nestjs
Strategy
Recommendation is to use @automapper/classes
in a NestJS application.
Usage
- Call
AutomapperModule.forRoot()
inAppModule
// single strategy
@Module({
imports: [
AutomapperModule.forRoot({
strategyInitializer: classes(),
}),
],
})
export class AppModule {}
// multiple strategies
@Module({
imports: [
AutomapperModule.forRoot(
[
{
name: 'classes',
strategyInitializer: classes(),
},
{
name: 'pojos',
strategyInitializer: pojos(),
},
],
{
globalErrorHandler,
globalNamingConventions,
}
),
],
})
export class AppModule {}
- Use
@InjectMapper()
to inject theMapper
in NestJS'sInjectable
@InjectMapper()
accepts an optional argumentname
. This is the name of theCreateMapperOptions
passed toAutomapperModule.forRoot()
AutomapperModule
is aGlobal
module, so it is only needed to be imported once to have theMapper
available across the application
Async Configuration
TBD: AutomapperModule.forRootAsync()
AutomapperProfile
AutomapperProfile
is an Injectable
in NestJS. Make sure to extends AutomapperProfile
import { AutomapperProfile } from '@automapper/nestjs';
import type { Mapper } from '@automapper/core';
import { Injectable } from '@nestjs/common';
@Injectable()
export class UserProfile extends AutomapperProfile {
constructor(@InjectMapper() mapper: Mapper) {
super(mapper);
}
override get profile() {
return (mapper) => {
createMap(mapper, User, UserDto);
};
}
}
Then provide UserProfile
in a Module
@Module({
providers: [UserProfile],
})
export class UserModule {}
AutomapperProfile
enforces the sub-classes to implement aget profile()
method that returns aMappingProfile
.AutomapperProfile
can have other Services injected to its constructor if needed.
mappingConfigurations
Same concept as Share Configuration using MappingProfile, AutomapperProfile
has an optional protected get mappingConfigurations()
that subclasses can override to provide an array of MappingConfiguration
. These configurations are passed to all createMap()
in get profile()
@Injectable()
export class UserProfile extends AutomapperProfile {
constructor(@InjectMapper() mapper: Mapper) {
super(mapper);
}
get profile(): MappingProfile {
return (mapper) => {
createMap(mapper, UserEntity, UserDto);
createMap(mapper, UserEntity, UserInformationDto);
createMap(mapper, UserEntity, AuthUserDto);
};
}
protected get mappingConfigurations(): MappingConfiguration[] {
// the 3 createMap() above will get this `extend()`
return [extend(BaseEntity, BaseDto)];
}
}
MapInterceptor
@automapper/nestjs
provides MapInterceptor
. In cases where you do not care about annotating the correct return type for a Controller#method and want your Service to be a little cleaner, you can utilize the MapInterceptor
to execute the mapping.
import { MapInterceptor } from '@automapper/nestjs';
export class UserController {
@Get('me')
@UseInterceptors(MapInterceptor(User, UserDto))
me() {
// userService.getMe() returns a User here and does not have mapping logic in it.
return this.userService.getMe();
}
}
MapInterceptor
has the following signature:
MapInterceptor(sourceModelType, destinationModelType, {
isArray?: boolean;
mapperName?: string;
} & MapOptions)
MapPipe
@automapper/nestjs
provides MapPipe
. When you want to transform the incoming request body before it gets to the route handler, you can utilize MapPipe
to achieve this behavior
@Post('/from-body')
postFromBody(@Body(MapPipe(User, UserDto)) user: UserDto) {
// from the request perspective, user coming in as an User object but will be mapped to UserDto with MapPipe
return user;
}
MapPipe
only works with @Body
or @Query
.
@Get('/from-query')
getFromQuery(@Query(MapPipe(User, UserDto)) user: UserDto) {
// from the request perspective, user coming in as an User object but will be mapped to UserDto with MapPipe
return user;
}
Note that when we send a request with
Body
orQuery
, the data is serialized. Data-type likeDate
will come in the request handler asstring
. Hence, please be cautious of the mapping configuration when you useMapPipe
MapPipe
has the same signature as MapInterceptor
MapPipe(sourceModelType, destinationModelType, {
isArray?: boolean;
mapperName?: string;
} & MapOptions)