When building full-stack applications with ASP.NET Core and Angular (or any frontend), you often deal with DTOs (Data Transfer Objects), ViewModels, and Entities. Mapping data between these objects manually can be repetitive, error-prone, and messy.
AutoMapper is a library that simplifies object-to-object mapping in ASP.NET Core. It automatically maps properties between objects with minimal configuration, making your backend code cleaner and maintainable.
This article explains how to use AutoMapper step by step in a full-stack application scenario.
What You Will Learn
What AutoMapper is and why it’s useful
How to install and configure AutoMapper in ASP.NET Core
How to create DTOs and map them to entities
How to integrate AutoMapper in your controllers and services
How AutoMapper improves frontend-backend communication
Best practices for clean, maintainable code
Step 1. Why Use AutoMapper
Suppose you have a User entity in your database:
public class User
{
public int Id { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
}
You don’t want to send the PasswordHash to the frontend. You may create a UserDto:
public class UserDto
{
public string FullName { get; set; }
public string Email { get; set; }
}
Without AutoMapper, you need to manually map:
var userDto = new UserDto
{
FullName = user.FullName,
Email = user.Email
};
For large objects or multiple endpoints, this becomes tedious. AutoMapper handles this automatically.
Part 1. Install Automapper
Run this command in your ASP.NET Core project:
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
Part 2. Create DTOs (Data Transfer Objects)
DTOs are lightweight objects that contain only the data you want to expose to the frontend.
Example
public class UserDto
{
public string FullName { get; set; }
public string Email { get; set; }
}
public class CreateUserDto
{
public string FullName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
Part 3. Configure Automapper
Create a Mapping Profile:
MappingProfile.cs
using AutoMapper;
public class MappingProfile : Profile
{
public MappingProfile()
{
// Map User -> UserDto
CreateMap<User, UserDto>();
// Map CreateUserDto -> User
CreateMap<CreateUserDto, User>()
.ForMember(dest => dest.PasswordHash, opt => opt.MapFrom(src => BCrypt.Net.BCrypt.HashPassword(src.Password)));
}
}
Step 2: Register AutoMapper in Startup
Program.cs (ASP.NET Core 6/7+)
using AutoMapper;
var builder = WebApplication.CreateBuilder(args);
// Add AutoMapper
builder.Services.AddAutoMapper(typeof(Program));
// Add other services
builder.Services.AddControllers();
builder.Services.AddDbContext<AppDbContext>(...);
var app = builder.Build();
app.MapControllers();
app.Run();
Part 4. Using Automapper in Controllers
Inject IMapper into your controller:
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly AppDbContext _context;
private readonly IMapper _mapper;
public UsersController(AppDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
// Get all users
[HttpGet]
public IActionResult GetUsers()
{
var users = _context.Users.ToList();
var usersDto = _mapper.Map<List<UserDto>>(users);
return Ok(usersDto);
}
// Create new user
[HttpPost]
public IActionResult CreateUser([FromBody] CreateUserDto createUserDto)
{
var user = _mapper.Map<User>(createUserDto);
_context.Users.Add(user);
_context.SaveChanges();
var userDto = _mapper.Map<UserDto>(user);
return Ok(userDto);
}
}
Part 5. Benefits in Full-Stack Developement
Cleaner backend code
AutoMapper removes repetitive manual mapping, making your controllers concise.
Consistent data shaping
You can ensure all endpoints return consistent DTOs.
Separation of concerns
Entities remain database-focused; DTOs remain API-focused.
Security
You can exclude sensitive fields like PasswordHash without manually removing them.
Easy maintenance
Changing property names in the database requires only updating the mapping profile.
Part 6. Integrating with Angular
Suppose you have an Angular frontend:
Service to get users
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class UserService {
constructor(private http: HttpClient) {}
getUsers() {
return this.http.get<User[]>('https://localhost:5001/api/users');
}
createUser(user: any) {
return this.http.post<User>('https://localhost:5001/api/users', user);
}
}
Component to display users
export class UsersComponent implements OnInit {
users: any[] = [];
constructor(private userService: UserService) {}
ngOnInit() {
this.userService.getUsers().subscribe((res: any) => this.users = res);
}
}
Because the backend uses AutoMapper to exclude sensitive fields, the frontend receives only the data it needs.
Part 7. Advanced mapping Techniques
1. Flattening Nested Objects
CreateMap<User, UserDto>()
.ForMember(dest => dest.RoleName, opt => opt.MapFrom(src => src.Role.Name));
2. Conditional Mapping
CreateMap<User, UserDto>()
.ForMember(dest => dest.Email, opt => opt.Condition(src => !string.IsNullOrEmpty(src.Email)));
3. Ignore Certain Properties
CreateMap<CreateUserDto, User>()
.ForMember(dest => dest.Id, opt => opt.Ignore());
Part 8. Best Practices
Use DTOs for all API responses
Keep MappingProfiles organized by domain
Avoid mapping large lists inside controller; map in services
Use Flattening and Conditional mapping for complex objects
Test your mappings with Unit Tests
Conclusion
Using AutoMapper in ASP.NET Core helps you:
Write cleaner, maintainable code
Avoid repetitive manual mapping
Separate your database entities from API responses
Safely send only necessary data to the frontend
For full-stack developers using Angular or React, AutoMapper ensures that your API consistently sends clean, secure, and structured data.