Building Clean & Minimal .NET APIs with Carter

July 29 2025

Replace the slow compile-debug cycle with Uno Platform's live UI editing. Uno Platform introduces Hot Design, a groundbreaking runtime visual designer that lets you pause your running .NET app, visually edit the UI, and resume instantly from any OS (Windows, macOS, Linux) using your favorite IDE like Visual Studio, VS Code, or Rider.
Give your cross-platform .NET apps the productive workflow they deserve with an open-source, single-codebase solution that goes far beyond basic Hot Reload.

 

Try it now

 

 
 

Background

 
 

In modern .NET development, there's been a trend toward minimal APIs - getting rid of controller bloat and focusing on just what matters. But as your app grows, even minimal APIs can become messy and hard to organize.

 

That’s where Carter comes in.

 

Carter gives you a clean, modular, and testable way to define your API endpoints - all while still using minimal APIs under the hood. It’s lightweight, composable, and plays nicely with your existing .NET stack.

 

In this article, we’ll walk through:
• Why Carter is useful
• How to set it up in a .NET 8+ project
• Real-world example with validation and mapping
• Pros & cons of using Carter
• Final thoughts

 
 

Why Carter?

 
 

Minimal APIs are great for small projects - but as your project grows:

 

• Routes scatter across multiple files.
• Dependency injection logic is repeated.
• Validation and mapping logic clutters route definitions.
• Testing individual endpoints becomes tricky.

 

Carter helps you modularize your endpoints into clean, testable components - without falling back to full MVC-style controllers.

 

 
 

Setting Up Carter in .NET

 
 

Start by installing the NuGet package:

dotnet add package Carter

 
 

Creating a Real-World Carter API

 
 

Let’s create a simple Products API where you can:
• Create a product
• Get a list of products
• Use FluentValidation for validation
• Use Custom Mapper for DTO -> Entity mapping

 
 

Project Structure

 
 


MyApi/
├── Program.cs
├── Endpoints/
│   └── ProductModule.cs
├── Models/
│   ├── Product.cs
│   └── ProductDto.cs
├── Validators/
│   └── CreateProductDtoValidator.cs

Product.cs


namespace MyApi.Models;

public class Product
{
    public Guid Id { get; set; }
    public string Name { get; set; } = default!;
    public decimal Price { get; set; }
}

ProductDto.cs


namespace MyApi.Models;

public record CreateProductDto(string Name, decimal Price);

CreateProductDtoValidator.cs


using FluentValidation;
using MyApi.Models;

namespace MyApi.Validators;

public class CreateProductDtoValidator : AbstractValidator<CreateProductDto>
{
    public CreateProductDtoValidator()
    {
        RuleFor(x => x.Name).NotEmpty().MaximumLength(100);
        RuleFor(x => x.Price).GreaterThan(0);
    }
}

ProductModule.cs

 

This makes your API modular, clean, and easy to maintain.

 

Every Carter module implements ICarterModule, which requires a single method: AddRoutes.

 

You inject services (like validators) directly into handlers.
It all compiles down to regular minimal API endpoints under the hood - just better organized!

using Carter;
using FluentValidation;
using Mapster;
using Microsoft.AspNetCore.Http.HttpResults;
using MyApi.Models;

namespace MyApi.Endpoints;

public class ProductModule : ICarterModule
{
    private static readonly List<Product> _products = [];

    public void AddRoutes(IEndpointRouteBuilder app)
    {
        app.MapGet("/products", () => _products);

        app.MapPost("/products", async (
            CreateProductDto dto,
            IValidator<CreateProductDto> validator) =>
        {
            var validationResult = await validator.ValidateAsync(dto);
            if (!validationResult.IsValid)
            {
                var errors = validationResult.Errors
                    .ToDictionary(e => e.PropertyName, e => e.ErrorMessage);
                return Results.BadRequest(errors);
            }

            var product = dto.ToProduct<Product>();
            product.Id = Guid.NewGuid();
            _products.Add(product);

            return Results.Created($"/products/{product.Id}", product);
        });
    }
}

 
 

Program.cs

 
 

That’s it! You now have a clean API with validation and mapping - and all routes are organized into modules.

using Carter;
using FluentValidation;
using MyApi.Models;
using MyApi.Validators;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCarter();
builder.Services.AddScoped<IValidator<CreateProductDto>, CreateProductDtoValidator>();

var app = builder.Build();

app.MapCarter(); // This maps all Carter modules automatically

app.Run();

 
 

Why Developers Love Carter

 
 

• Keeps things clean and organized

 

• Grows well with your project
What starts as a small API can get messy fast. Carter gives your project structure, even as it grows.

 

• You can inject anything you need
Need a service? A validator? A logger? Just inject it directly into your route - no extra setup, no controller boilerplate.

 

• Works great with modern patterns
If you're into vertical slice architecture, CQRS, or minimal APIs - Carter plays really well with those styles.

 

• Cleaner startup
Your Program.cs stays short and sweet. Just call app.MapCarter() and all your modules are wired up.

 

• Fast and lightweight
There’s no controller or attribute overhead. It’s just minimal APIs - but more organized.

 

• Easy to test
Because modules are simple classes, they’re super easy to write tests for - no magic or reflection behind the scenes.

 

• Flexible and plug-and-play
Use whatever tools you like - FluentValidation, Mapster, MediatR, Dapper, EF Core… Carter doesn’t get in your way.

 
 

Things to Keep in Mind

 
 

• Smaller community, less documentation
Carter is awesome, but it’s not as mainstream as .NET MVC. You might not find answers on Stack Overflow as quickly.

 

• You define routes manually
There’s no [HttpGet], [Route], or attribute-based routing here - just regular method calls like MapGet. Some people miss the attributes.

 

• No fancy model binding attributes
You won’t see [FromBody] or [FromQuery]. Binding works like in minimal APIs - clean, but different if you're used to MVC.

 

• You have to decide how to structure things
Carter gives you flexibility, but with that comes responsibility. You’ll need to create your own structure for modules, validators, etc.

 

• No built-in filters or attributes
If you're used to using [Authorize], [ValidateModel], or action filters, you’ll have to wire up that behavior yourself.

 
 

Wrapping Up

 
 

Carter is one of those libraries that makes you think, “Why didn’t I use this earlier?” It takes the flexibility of minimal APIs and adds just the right amount of structure - without dragging you back into the world of bloated controllers and attributes.

 

If you're building APIs that are starting to grow beyond a few routes, or you just want a cleaner way to organize your features, Carter is 100% worth trying out.

 

It’s simple, lightweight, and fits beautifully into modern .NET projects.

 

Sure, it’s not as mainstream as MVC, and you’ll need to define some structure on your own -nbut that freedom is part of what makes it so powerful.

 

So the next time you find yourself deep in a messy Program.cs file wondering where to put that new endpoint... maybe give Carter a shot.

 

You might just fall in love with how clean your code starts to feel.

 

That's all from me today.

 

P.S. Follow me on YouTube.

There are 3 ways I can help you:

My Design Patterns Ebooks

1. Design Patterns that Deliver

This isn’t just another design patterns book. Dive into real-world examples and practical solutions to real problems in real applications.Check out it here.


1. Design Patterns Simplified

Go-to resource for understanding the core concepts of design patterns without the overwhelming complexity. In this concise and affordable ebook, I've distilled the essence of design patterns into an easy-to-digest format. It is a Beginner level. Check out it here.


Join TheCodeMan.net Newsletter

Every Monday morning, I share 1 actionable tip on C#, .NET & Arcitecture topic, that you can use right away.


Sponsorship

Promote yourself to 17,150+ subscribers by sponsoring this newsletter.



Join 17,150+ subscribers to improve your .NET Knowledge.

Powered by EmailOctopus

Subscribe to
TheCodeMan.net

Subscribe to the TheCodeMan.net and be among the 17,150+ subscribers gaining practical tips and resources to enhance your .NET expertise.

Powered by EmailOctopus