Introduction
In modern web applications, APIs are the backbone of data exchange between the backend and the frontend. However, sending entire database entities directly as API responses often leads to:
Large payloads
Unnecessary data exposure
Performance bottlenecks in slow networks
A common solution in ASP.NET Core applications is to use DTOs (Data Transfer Objects) combined with AutoMapper. This approach allows developers to:
Return only the required fields
Map complex entities to simpler shapes
Reduce bandwidth consumption
Maintain separation of concerns
In this article, we will demonstrate a complete workflow with ASP.NET Core APIs, AutoMapper, and Angular clients, ensuring production-ready implementation.
1. Understanding DTOs
DTO (Data Transfer Object) is a simple object designed to transfer data between layers, especially between backend and frontend.
Benefits of using DTOs:
Minimized API payloads – Send only the fields required by the client.
Encapsulation – Hide internal fields like passwords or audit logs.
Decoupling – Backend can change entity structure without breaking the API contract.
Improved performance – Smaller JSON payloads reduce latency and bandwidth.
Example
Suppose you have a User entity:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
public DateTime CreatedAt { get; set; }
public string Role { get; set; }
}
You don’t want to send PasswordHash or internal timestamps to the frontend. Instead, define a DTO:
public class UserDto
{
public int Id { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
}
2. Introducing AutoMapper
AutoMapper is a library for object-to-object mapping. It automatically maps properties from a source object to a target object, reducing boilerplate code.
Installation
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
Configuration
Create a Mapping Profile:
using AutoMapper;
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<User, UserDto>()
.ForMember(dest => dest.FullName,
opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}"));
}
}
Register AutoMapper in Program.cs:
builder.Services.AddAutoMapper(typeof(Program));
3. Using DTOs in ASP.NET Core Controllers
Example: User API
[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;
}
[HttpGet]
public async Task<IActionResult> GetUsers()
{
var users = await _context.Users.ToListAsync();
// Map entities to DTOs
var userDtos = _mapper.Map<List<UserDto>>(users);
return Ok(userDtos);
}
}
Key Points
The frontend receives only
Id,FullName, andEmail.PasswordHashand internal fields are never exposed.Mapping is automatic and maintainable even with nested objects.
4. Mapping Nested Objects
Many real-world entities have relationships. AutoMapper can handle nested properties.
Example: Product and Category
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
DTO
public class ProductDto
{
public int Id { get; set; }
public string Name { get; set; }
public string CategoryName { get; set; }
}
Mapping Profile
CreateMap<Product, ProductDto>()
.ForMember(dest => dest.CategoryName, opt => opt.MapFrom(src => src.Category.Name));
Now, the API will send a flat, lightweight object instead of embedding the entire Category.
5. Reducing API Response Size With Select Queries
Instead of fetching full entities and then mapping, project directly to DTOs using LINQ. This is more efficient for large datasets.
[HttpGet]
public async Task<IActionResult> GetProducts()
{
var products = await _context.Products
.Select(p => new ProductDto
{
Id = p.Id,
Name = p.Name,
CategoryName = p.Category.Name
})
.ToListAsync();
return Ok(products);
}

Join the conversation! Your thoughts help the community grow.