A DTO is a plain C# class used to transfer data between layers (e.g., from your API to your Blazor client). It helps decouple your internal models from what you expose externally.
Create DTOs in a Shared Project
To share DTOs between your Blazor WASM and API:
- Create a Shared project (e.g.,
MyApp.Shared) - Add DTO classes there so both the Blazor client and API can reference them.
// MyApp.Shared/DTOs/ProductDto.cs
public class ProductDto
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
}
In your ASP.NET Core Web API:
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet]
public ActionResult<List<ProductDto>> GetProducts()
{
var products = new List<ProductDto>
{
new ProductDto { Id = 1, Name = "Widget", Price = 9.99M },
new ProductDto { Id = 2, Name = "Gadget", Price = 19.99M }
};
return Ok(products);
}
In your Blazor client project:
@inject HttpClient Http
@code {
private List<ProductDto>? products;
protected override async Task OnInitializedAsync()
{
products = await Http.GetFromJsonAsync<List<ProductDto>>("api/products");
}
}
Or use a service class:
public class ProductService
{
private readonly HttpClient _http;
public ProductService(HttpClient http)
{
_http = http;
}
public async Task<List<ProductDto>?> GetProductsAsync()
{
return await _http.GetFromJsonAsync<List<ProductDto>>("api/products");
}
}
Tips for DTO Design
- Keep DTOs flat and simple.
- Avoid exposing domain entities directly.
- Use nullable types if fields may be missing.
- Use records if immutability is preferred:

