🔥 Pragmatic .NET Code Rules Course is on Presale - 40% off!BUY NOW

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:

C#
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

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

Product.cs

C#
namespace MyApi.Models; public class Product{ public Guid Id { get; set; } public string Name { get; set; } = default!; public decimal Price { get; set; }}

ProductDto.cs

C#
namespace MyApi.Models; public record CreateProductDto(string Name, decimal Price);

CreateProductDtoValidator.cs

C#
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

In Carter, instead of defining routes directly in Program.cs or using controllers, you create "modules" that group related endpoints. These modules implement the ICarterModule interface. 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. You keep everything related to “Product” inside one file/module. It all compiles down to regular minimal API endpoints under the hood - just better organized!

C#
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.

C#
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 Instead of cluttering your Program.cs with dozens of routes, Carter lets you group related endpoints in neat modules. It just feels better.

• 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.


Building APIs like this? Grab my free Vertical Slice Architecture template - a clean .NET 10 project with 10 endpoints and zero unnecessary abstractions.

About the Author

Stefan Djokic is a Microsoft MVP and senior .NET engineer with extensive experience designing enterprise-grade systems and teaching architectural best practices.

There are 3 ways I can help you:

1. Pragmatic .NET Code Rules Course

Stop arguing about code style. In this course you get a production-proven setup with analyzers, CI quality gates, and architecture tests — the exact system I use in real projects. Join here.

Not sure yet? Grab the free Starter Kit — a drop-in setup with the essentials from Module 01.

2. Design Patterns Ebooks

Design Patterns that Deliver — Solve real problems with 5 battle-tested patterns (Builder, Decorator, Strategy, Adapter, Mediator) using practical, real-world examples. Trusted by 650+ developers.

Just getting started? Design Patterns Simplified covers 10 essential patterns in a beginner-friendly, 30-page guide for just $9.95.

3. Join 20,000+ subscribers

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

Join 20,000+ subscribers who mass-improve their .NET skills with actionable tips on C#, Architecture & Best Practices.

Subscribe to
TheCodeMan.net

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