# ASP.NET Core Minimal APIs 2026: Arsitektur, Performa dan Pertanyaan Interview > Panduan lengkap ASP.NET Core Minimal APIs dengan arsitektur modern, optimasi performa Native AOT, dan pertanyaan interview umum untuk developer .NET. - Published: 2026-07-24 - Updated: 2026-07-24 - Author: SharpSkill - Reading time: 5 min --- ASP.NET Core Minimal APIs menghilangkan boilerplate yang berlebihan dari controller MVC tradisional, menawarkan pendekatan yang lebih ringkas untuk membangun HTTP endpoint. Diperkenalkan di .NET 6 dan disempurnakan melalui .NET 8, 9, hingga .NET 10, Minimal APIs telah matang menjadi pilihan production-ready untuk microservices, serverless functions, dan lightweight web services. > **Minimal APIs vs Controllers** > > Minimal APIs menggunakan top-level route handlers yang didefinisikan langsung di Program.cs, sedangkan MVC controllers memerlukan definisi class, attributes, dan convention-based routing. Untuk operasi CRUD sederhana atau microservices dengan kurang dari 20 endpoint, Minimal APIs biasanya mengurangi kode sebesar 40-60%. ## Arsitektur Minimal API dan Request Pipeline Request pipeline ASP.NET Core memproses HTTP requests melalui komponen middleware sebelum mencapai endpoint handlers. Minimal APIs terintegrasi dengan baik dalam pipeline ini sambil menawarkan sintaks yang lebih deklaratif untuk definisi route. ```csharp // Program.cs var builder = WebApplication.CreateBuilder(args); // Register services for dependency injection builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddScoped(); var app = builder.Build(); // Middleware pipeline configuration app.UseExceptionHandler("/error"); app.UseHttpsRedirection(); app.UseAuthorization(); // Minimal API endpoint definitions app.MapGet("/products", async (IProductRepository repo) => Results.Ok(await repo.GetAllAsync())); app.MapGet("/products/{id:int}", async (int id, IProductRepository repo) => await repo.GetByIdAsync(id) is Product product ? Results.Ok(product) : Results.NotFound()); app.Run(); ``` Pola ini memusatkan definisi route sambil mempertahankan akses penuh ke dependency injection container. Parameter `IProductRepository` mendemonstrasikan constructor-less injection langsung ke dalam handler delegates. ## Route Groups dan Organisasi Endpoint Seiring pertumbuhan aplikasi, pengorganisasian endpoint menjadi sangat penting. Route groups, diperkenalkan di .NET 7, menyediakan namespacing dan konfigurasi bersama tanpa mengorbankan pendekatan minimal. ```csharp // ProductEndpoints.cs public static class ProductEndpoints { public static void MapProductEndpoints(this WebApplication app) { var group = app.MapGroup("/api/products") .WithTags("Products") .RequireAuthorization(); group.MapGet("/", GetAllProducts); group.MapGet("/{id:int}", GetProductById); group.MapPost("/", CreateProduct) .Accepts("application/json") .Produces(StatusCodes.Status201Created); group.MapPut("/{id:int}", UpdateProduct); group.MapDelete("/{id:int}", DeleteProduct) .RequireAuthorization("AdminOnly"); } private static async Task GetAllProducts( IProductRepository repo, CancellationToken ct) { var products = await repo.GetAllAsync(ct); return Results.Ok(products); } private static async Task GetProductById( int id, IProductRepository repo, CancellationToken ct) { var product = await repo.GetByIdAsync(id, ct); return product is not null ? Results.Ok(product) : Results.NotFound(); } private static async Task CreateProduct( CreateProductRequest request, IProductRepository repo, IValidator validator, CancellationToken ct) { var validation = await validator.ValidateAsync(request, ct); if (!validation.IsValid) return Results.ValidationProblem(validation.ToDictionary()); var product = await repo.CreateAsync(request.ToProduct(), ct); return Results.Created($"/api/products/{product.Id}", product); } } ``` Memanggil `app.MapProductEndpoints()` di Program.cs mendaftarkan semua product routes dengan shared authorization requirements dan OpenAPI metadata. Struktur ini berskala baik untuk aplikasi dengan ratusan endpoint. ## Parameter Binding dan Validasi Minimal APIs mendukung berbagai binding sources: route parameters, query strings, headers, request bodies, dan services dari DI. Memahami binding precedence mencegah kesalahan umum dalam interview. ```csharp // Program.cs - Parameter binding examples app.MapGet("/search", ( [FromQuery] string? query, // Explicit query string [FromQuery] int page = 1, // Default value [FromQuery] int pageSize = 20, // Default value [FromHeader(Name = "X-Correlation-Id")] string? correlationId, ILogger logger) => { logger.LogInformation("Search request: {Query}, Page: {Page}, CorrelationId: {CorrelationId}", query, page, correlationId); return Results.Ok(new { query, page, pageSize, correlationId }); }); // Complex model binding with validation app.MapPost("/orders", async ( [FromBody] CreateOrderRequest request, [FromServices] IValidator validator, [FromServices] IOrderService orderService, HttpContext context, CancellationToken ct) => { var validationResult = await validator.ValidateAsync(request, ct); if (!validationResult.IsValid) { return Results.ValidationProblem( validationResult.Errors .GroupBy(e => e.PropertyName) .ToDictionary( g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray())); } var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier); var order = await orderService.CreateOrderAsync(request, userId!, ct); return Results.Created($"/orders/{order.Id}", order); }); ``` Attribute `[FromBody]` bersifat opsional untuk complex types namun meningkatkan keterbacaan. FluentValidation terintegrasi secara natural melalui dependency injection, memisahkan logika validasi dari endpoint handlers. > **Prioritas Binding Source** > > Ketika tidak ada attribute yang ditentukan, Minimal APIs menyimpulkan binding sources: route parameters terlebih dahulu, kemudian query strings untuk simple types, dan request body untuk complex types. Explicit attributes seperti [FromQuery] atau [FromBody] mengganti perilaku ini. ## Optimasi Performa dengan Native AOT .NET 8 memperkenalkan dukungan Native AOT (Ahead-of-Time) compilation untuk Minimal APIs, menghasilkan executable mandiri dengan waktu startup sub-milidetik. Kemampuan ini menjadikan Minimal APIs ideal untuk deployment serverless di mana cold start latency sangat penting. ```csharp // Program.cs - AOT-compatible configuration var builder = WebApplication.CreateSlimBuilder(args); // AOT-friendly JSON serialization builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default); }); var app = builder.Build(); app.MapGet("/health", () => Results.Ok(new HealthResponse("Healthy", DateTime.UtcNow))); app.Run(); // Source-generated JSON serializer context [JsonSerializable(typeof(HealthResponse))] [JsonSerializable(typeof(Product))] [JsonSerializable(typeof(List))] internal partial class AppJsonContext : JsonSerializerContext { } public record HealthResponse(string Status, DateTime CheckedAt); ``` Method `CreateSlimBuilder` mengecualikan fitur framework yang tidak diperlukan, sementara source-generated `JsonSerializerContext` mengeliminasi runtime reflection untuk JSON serialization. Published AOT binaries untuk simple APIs biasanya berukuran 10-15 MB dibandingkan 80+ MB untuk standard self-contained deployments. ## Typed Results dan Response Metadata .NET 7 memperkenalkan `TypedResults` untuk verifikasi compile-time dari response types, meningkatkan akurasi dokumentasi OpenAPI dan mendeteksi type mismatches selama development. ```csharp // Strongly-typed results with OpenAPI metadata app.MapGet("/products/{id:int}", async Task, NotFound, ProblemHttpResult>> ( int id, IProductRepository repo, CancellationToken ct) => { try { var product = await repo.GetByIdAsync(id, ct); return product is not null ? TypedResults.Ok(product) : TypedResults.NotFound(); } catch (Exception ex) { return TypedResults.Problem( detail: "An error occurred retrieving the product", statusCode: StatusCodes.Status500InternalServerError); } }) .WithName("GetProductById") .WithOpenApi(operation => { operation.Summary = "Retrieves a product by ID"; operation.Description = "Returns the product details or 404 if not found"; return operation; }); ``` Union type `Results` mendeklarasikan semua kemungkinan response types, yang digunakan Swagger/OpenAPI generators untuk menghasilkan dokumentasi yang akurat. Pola ini sangat berharga ketika mempersiapkan pertanyaan interview tentang API design. ## Endpoint Filters untuk Cross-Cutting Concerns Endpoint filters menyediakan fungsionalitas mirip middleware yang dibatasi pada endpoint atau groups tertentu, menangani concerns seperti logging, caching, dan request transformation. ```csharp // ValidationFilter.cs public class ValidationFilter : IEndpointFilter where T : class { public async ValueTask InvokeAsync( EndpointFilterInvocationContext context, EndpointFilterDelegate next) { var validator = context.HttpContext .RequestServices .GetService>(); if (validator is null) return await next(context); var argument = context.Arguments .OfType() .FirstOrDefault(); if (argument is null) return await next(context); var validationResult = await validator.ValidateAsync(argument); if (!validationResult.IsValid) { return Results.ValidationProblem( validationResult.Errors .GroupBy(e => e.PropertyName) .ToDictionary( g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray())); } return await next(context); } } // Usage in Program.cs app.MapPost("/products", CreateProduct) .AddEndpointFilter>(); // Global filter registration via route group var api = app.MapGroup("/api") .AddEndpointFilter(async (context, next) => { var logger = context.HttpContext .RequestServices .GetRequiredService>(); var stopwatch = Stopwatch.StartNew(); var result = await next(context); stopwatch.Stop(); logger.LogInformation( "Endpoint {Method} {Path} completed in {ElapsedMs}ms", context.HttpContext.Request.Method, context.HttpContext.Request.Path, stopwatch.ElapsedMilliseconds); return result; }); ``` Filters dieksekusi sesuai urutan registrasi, dengan innermost filter paling dekat dengan endpoint handler. Arsitektur ini memungkinkan pemisahan yang bersih dari logika validasi, logging, dan authorization. ## Pola Authentication dan Authorization Minimal APIs mendukung mekanisme authentication dan authorization yang sama dengan MVC controllers, dengan sintaks konfigurasi yang lebih deklaratif. ```csharp // Program.cs - JWT authentication setup builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey( Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!)) }; }); builder.Services.AddAuthorizationBuilder() .AddPolicy("AdminOnly", policy => policy.RequireRole("Admin")) .AddPolicy("PremiumUser", policy => policy.RequireClaim("subscription", "premium", "enterprise")); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); // Protected endpoints app.MapGet("/admin/users", async (IUserService userService) => Results.Ok(await userService.GetAllUsersAsync())) .RequireAuthorization("AdminOnly"); app.MapGet("/profile", async (ClaimsPrincipal user, IUserService userService) => { var userId = user.FindFirstValue(ClaimTypes.NameIdentifier); var profile = await userService.GetProfileAsync(userId!); return Results.Ok(profile); }) .RequireAuthorization(); // Anonymous endpoint within protected group var protectedGroup = app.MapGroup("/api/secure") .RequireAuthorization(); protectedGroup.MapGet("/public-info", () => Results.Ok("This is public")) .AllowAnonymous(); ``` Extension method `RequireAuthorization` menerima nama policy atau dapat dipanggil tanpa argumen untuk memerlukan user terauthentikasi. Memahami pola-pola ini sangat penting untuk pertanyaan interview tentang authentication dan authorization. ## Pertanyaan Interview: Pola Umum Interview teknis sering mengeksplorasi perbedaan antara Minimal APIs dan traditional controllers. Tabel berikut merangkum perbedaan utama: | Aspek | Minimal APIs | MVC Controllers | |-------|--------------|----------------| | Boilerplate | Rendah - handler delegates langsung | Lebih tinggi - class + method + attributes | | Routing | Inline dengan `MapGet`, `MapPost` | Attribute atau convention-based | | Model Binding | Otomatis dengan optional attributes | Convention + attributes | | Filters | Endpoint filters | Action filters + middleware | | Dukungan AOT | Penuh sejak .NET 8 | Terbatas, reflection-heavy | | Testability | Function-based, mudah di-unit test | Memerlukan instantiasi controller | | Cocok Untuk | Microservices, simple APIs | Aplikasi besar, complex workflows | > **Tips Interview** > > Ketika ditanya "Kapan memilih controllers dibanding Minimal APIs?", sebutkan aplikasi kompleks yang memerlukan action filters, kustomisasi model binding, atau codebase yang sudah ada dengan pola MVC yang established. Minimal APIs unggul untuk greenfield microservices dan serverless functions. ## Testing Endpoint Minimal API Integration testing dengan `WebApplicationFactory` menyediakan verifikasi endpoint yang realistis tanpa men-deploy aplikasi. ```csharp // ProductEndpointsTests.cs public class ProductEndpointsTests : IClassFixture> { private readonly HttpClient _client; private readonly WebApplicationFactory _factory; public ProductEndpointsTests(WebApplicationFactory factory) { _factory = factory.WithWebHostBuilder(builder => { builder.ConfigureServices(services => { // Replace real repository with mock services.RemoveAll(); services.AddScoped(); }); }); _client = _factory.CreateClient(); } [Fact] public async Task GetProducts_ReturnsOkWithProductList() { // Act var response = await _client.GetAsync("/api/products"); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var products = await response.Content .ReadFromJsonAsync>(); products.Should().NotBeNull(); products.Should().HaveCountGreaterThan(0); } [Fact] public async Task GetProductById_WithInvalidId_ReturnsNotFound() { // Act var response = await _client.GetAsync("/api/products/99999"); // Assert response.StatusCode.Should().Be(HttpStatusCode.NotFound); } [Fact] public async Task CreateProduct_WithValidRequest_ReturnsCreated() { // Arrange var request = new CreateProductRequest("Test Product", 29.99m, "Test Description"); // Act var response = await _client.PostAsJsonAsync("/api/products", request); // Assert response.StatusCode.Should().Be(HttpStatusCode.Created); response.Headers.Location.Should().NotBeNull(); } } ``` Untuk eksplorasi lebih mendalam tentang clean architecture patterns dalam aplikasi .NET, service layer sebaiknya diuji secara independen dengan unit tests, sementara integration tests memverifikasi full request pipeline. ## Kesimpulan Minimal APIs merepresentasikan pendekatan modern untuk membangun HTTP services di ASP.NET Core: - Route groups dan organisasi endpoint berskala dari microservices sederhana hingga aplikasi kompleks - Endpoint filters menyediakan pemisahan yang bersih dari cross-cutting concerns seperti validasi dan logging - TypedResults memungkinkan verifikasi compile-time response type dan dokumentasi OpenAPI yang akurat - Native AOT compilation menghadirkan waktu startup sub-milidetik untuk serverless deployments - Integration testing dengan WebApplicationFactory memastikan verifikasi endpoint yang realistis Untuk persiapan interview, fokus pada mengartikulasikan kapan Minimal APIs tepat dibandingkan traditional controllers, dan demonstrasikan pemahaman tentang request pipeline, dependency injection, dan authentication patterns. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/id/blog/dotnet/aspnet-core-minimal-apis-architecture-performance-interview