Skip to main content

Use Mapping Configurations

fullName

In this particular case, you can call fullName a computed property because the Source (User) does not have a fullName property. Hence, it must be computed from something else. To configure the mapping instruction for a property, you can use forMember() with createMap()

import { createMap, forMember, mapFrom } from '@automapper/core';

createMap(
mapper,
User,
UserDto,
forMember(
(destination) => destination.fullName,
mapFrom((source) => source.firstName + ' ' + source.lastName)
)
);

forMember() accepts two arguments:

  • A Selector to select the property you want to configure the instruction for.
  • A MemberMapFn that carries a specific instruction.
info

Read more about ForMember

Here, forMember() is saying: For destination.fullName, use source.firstName + ' ' + source.lastName as the result.

birthday

For birthday, let's try using another MappingConfiguration function: typeConverter. Here, we want to convert from Date to string.

createMap(
mapper,
Bio,
BioDto,
typeConverter(Date, String, (date) => date.toDateString())
);

typeConverter() is saying: If a property on the Source is of type Date and the matching property on the Destination is of type String, use the conversion of date.toDateString()

note
  • typeConverter will apply Date -> String conversion for ALL pairs of Date, String, not just birthday
  • Instead of typeConverter, you can also use forMember() to map BioDto#birthday by itself.

jobTitle and jobSalary

These two properties are a bit different from fullName and birthday. If you notice, you will see that jobTitle and jobSalary are flatten properties of job.title and job.salary.

AutoMapper supports Auto Flattening out of the box with the concept of Naming Conventions. With that in mind, let's provide a NamingConvention for Mapping<Bio, BioDto> by using yet another MappingConfiguration function: namingConventions()

createMap(
mapper,
Bio,
BioDto,
typeConverter(Date, String, (date) => date.toDateString()),
namingConventions(new CamelCaseNamingConvention())
);

Let's execute the map operation again, you'll get the complete UserDto 🎉

UserDto {
bio: BioDto {
avatarUrl: 'google.com',
birthday: 'Wed Mar 23 2022',
jobSalary: 99999,
jobTitle: 'Developer'
},
username: 'ctran',
lastName: 'Tran',
firstName: 'Chau',
fullName: 'Chau Tran'
}

Summary

  • Without AutoMapper, mapping logic is repetitive and hard to scale. DTOs and Entities are coupling.
  • With AutoMapper, matching properties are mapped automatically (firstName, lastName, username, bio, and bio.avatarUrl)
  • Auto Flattening with Naming Conventions
  • Type Converter allows for using the same conversion for the same pair of types.
export class User {
@AutoMap()
firstName: string;

@AutoMap()
lastName: string;

@AutoMap()
username: string;

password: string; // <- we purposely left this one out because we don't want to map "password"

@AutoMap(() => Bio)
bio: Bio;
}

export class Bio {
@AutoMap(() => Job)
job: Job;

@AutoMap()
birthday: Date;

@AutoMap()
avatarUrl: string;
}

export class Job {
@AutoMap()
title: string;

@AutoMap()
salary: number;
}