S

super-dev — full-stack

@super-dev.app · Full-stack · ★ ouvert aux missions
12,8k abonnés·47 vidéos·Actif depuis 2017

Développeur full-stack .NET / Angular avec une casquette DevOps sur Azure Cloud. Aussi à l'aise sur Flutter + Firebase. Je construis des produits, je les déploie, je les surveille.

Retour aux articles
ƒ()
.NET
.NET

Minimal APIs + EF Core : une API .NET 8 propre

Article 2 sur 5 — Le .NET moderne
APIs minimales, CQRS, gRPC, source generators : du .NET 8 net et testable, sans usine à gaz.

Les Minimal APIs ont mauvaise réputation : on les croit réservées aux démos jetables. En réalité, avec un peu de discipline, elles donnent une API .NET 8 plus lisible et plus testable qu'un contrôleur classique — à condition de ne pas tout entasser dans Program.cs .

Découper avec des route groups

Le piège du débutant, c'est d'empiler trente app.MapGet dans le Program.cs . La parade tient en un mot : `MapGroup` . Chaque ressource a son groupe, avec son préfixe, ses filtres et ses métadonnées, défini dans une méthode d'extension dédiée :

C#
1public static class TodoEndpoints
2{
3 public static RouteGroupBuilder MapTodos(this IEndpointRouteBuilder app)
4 {
5 var group = app.MapGroup("/todos")
6 .WithTags("Todos")
7 .WithOpenApi();
8
9 group.MapGet("/", GetAllAsync);
10 group.MapGet("/{id:int}", GetByIdAsync);
11 group.MapPost("/", CreateAsync);
12
13 return group;
14 }
15
16 private static async Task<Ok<List<Todo>>> GetAllAsync(AppDbContext db) =>
17 TypedResults.Ok(await db.Todos.AsNoTracking().ToListAsync());
18}

Le Program.cs se résume alors à app.MapTodos(); — un point d'entrée par ressource, le reste vit dans des fichiers cohérents.

DbContext et migrations

EF Core reste la colonne vertébrale de l'accès aux données. On enregistre le DbContext via AddDbContext , on modélise dans OnModelCreating , et surtout on ne laisse jamais le schéma dériver à la main : chaque changement passe par une migration versionnée.

C#
1builder.Services.AddDbContext<AppDbContext>(options =>
2 options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));

On génère ensuite la migration avec dotnet ef migrations add InitialCreate , et on l'applique au démarrage avec db.Database.MigrateAsync() — jamais EnsureCreated , qui court-circuite tout l'historique. La doc officielle détaille le workflow dans le guide EF Core migrations .

Résultats typés et validation

C'est ici que les Minimal APIs gagnent vraiment. Plutôt que de renvoyer un IActionResult opaque, on retourne un union de résultats typés : la signature documente les codes HTTP possibles, et OpenAPI les expose automatiquement.

C#
1private static async Task<Results<Created<Todo>, ValidationProblem>> CreateAsync(
2 CreateTodoRequest request, AppDbContext db)
3{
4 if (string.IsNullOrWhiteSpace(request.Title))
5 {
6 return TypedResults.ValidationProblem(new Dictionary<string, string[]>
7 {
8 ["title"] = ["Le titre est obligatoire."],
9 });
10 }
11
12 var todo = new Todo { Title = request.Title };
13
14 db.Todos.Add(todo);
15 await db.SaveChangesAsync();
16
17 return TypedResults.Created($"/todos/{todo.Id}", todo);
18}

Le type de retour Results<Created<Todo>, ValidationProblem> est auto-documentant : pas besoin d'attributs [ProducesResponseType] redondants.

Garder le tout testable

Une fois les handlers extraits en méthodes statiques qui reçoivent leurs dépendances en paramètres, ils deviennent triviaux à tester sans serveur HTTP : on instancie un AppDbContext sur le provider in-memory ou SQLite, on appelle le handler, on inspecte le TypedResults . Pour les tests d'intégration de bout en bout, WebApplicationFactory<T> monte l'application complète en mémoire et permet de taper les vrais endpoints.

Une Minimal API n'est pas une API au rabais. Bien découpée en groupes et en résultats typés, elle expose moins de cérémonie pour plus de garanties — et c'est exactement ce qu'on veut d'un framework moderne.
super-dev — portfolio.app
// Continuer dans .NET
{ }
Étrangler un monolithe .NET 8 sans tout casser
9 min • 3,2k lectures
CQRS et vertical slices sans usine à gaz
7 min • 2,3k lectures
gRPC entre microservices .NET
9 min • 1,8k lectures