# HTTP Integration Overview Expose commands and queries via HTTP using ASP.NET Core Minimal API. ## What is HTTP Integration? The `Svrnty.CQRS.MinimalApi` package automatically generates HTTP endpoints for all registered commands and queries using ASP.NET Core Minimal API. **Key Features:** - ✅ **Automatic endpoint generation** - No manual controller code - ✅ **Convention-based routing** - Predictable URL patterns - ✅ **Swagger/OpenAPI support** - Automatic API documentation - ✅ **Flexible methods** - POST for commands, GET/POST for queries - ✅ **Built-in validation** - RFC 7807 Problem Details - ✅ **Authorization support** - Integrated authorization services ## Quick Start ### Installation ```bash dotnet add package Svrnty.CQRS.MinimalApi ``` ### Basic Setup ```csharp var builder = WebApplication.CreateBuilder(args); // Register CQRS services builder.Services.AddSvrntyCQRS(); builder.Services.AddDefaultCommandDiscovery(); builder.Services.AddDefaultQueryDiscovery(); // Register commands and queries builder.Services.AddCommand(); builder.Services.AddQuery(); var app = builder.Build(); // Map CQRS endpoints app.MapSvrntyCommands(); // POST /api/command/{name} app.MapSvrntyQueries(); // GET/POST /api/query/{name} app.Run(); ``` **This creates endpoints automatically:** - `POST /api/command/createUser` - `GET /api/query/getUser?userId=123` - `POST /api/query/getUser` ## How It Works ``` ┌────────────────────┐ │ HTTP Request │ │ POST /api/command │ │ /createUser │ └─────────┬──────────┘ │ ▼ ┌────────────────────┐ │ Model Binding │ │ JSON → Command │ └─────────┬──────────┘ │ ▼ ┌────────────────────┐ │ Validation │ │ IValidator │ └─────────┬──────────┘ │ ▼ ┌────────────────────┐ │ Authorization │ │ ICommandAuth... │ └─────────┬──────────┘ │ ▼ ┌────────────────────┐ │ Handler │ │ ICommandHandler │ └─────────┬──────────┘ │ ▼ ┌────────────────────┐ │ HTTP Response │ │ 200 OK / 400 Bad │ └────────────────────┘ ``` ## Commands via HTTP ### Command Without Result ```csharp public record DeleteUserCommand { public int UserId { get; init; } } public class DeleteUserCommandHandler : ICommandHandler { public async Task HandleAsync(DeleteUserCommand command, CancellationToken cancellationToken) { // Delete user logic } } ``` **HTTP Request:** ```bash curl -X POST http://localhost:5000/api/command/deleteUser \ -H "Content-Type: application/json" \ -d '{"userId": 123}' ``` **Response:** ``` HTTP/1.1 204 No Content ``` ### Command With Result ```csharp public record CreateUserCommand { public string Name { get; init; } = string.Empty; public string Email { get; init; } = string.Empty; } public class CreateUserCommandHandler : ICommandHandler { public async Task HandleAsync(CreateUserCommand command, CancellationToken cancellationToken) { // Create user and return ID return newUserId; } } ``` **HTTP Request:** ```bash curl -X POST http://localhost:5000/api/command/createUser \ -H "Content-Type: application/json" \ -d '{"name": "John Doe", "email": "john@example.com"}' ``` **Response:** ```json 42 ``` ## Queries via HTTP Queries support **both GET and POST** methods. ### GET with Query String ```bash GET /api/query/getUser?userId=123 ``` **Advantages:** - Cacheable - Bookmarkable - Simple for basic queries **Limitations:** - URL length limits - No complex objects - Visible in logs/browser history ### POST with JSON Body ```bash POST /api/query/getUser Content-Type: application/json {"userId": 123} ``` **Advantages:** - Complex objects - No URL length limits - Sensitive data in body **Use Cases:** - Search with multiple filters - Pagination parameters - Complex query objects ### Example Query ```csharp public record GetUserQuery { public int UserId { get; init; } } public record UserDto { public int Id { get; init; } public string Name { get; init; } = string.Empty; public string Email { get; init; } = string.Empty; } public class GetUserQueryHandler : IQueryHandler { public async Task HandleAsync(GetUserQuery query, CancellationToken cancellationToken) { // Fetch and return user } } ``` **GET Request:** ```bash curl http://localhost:5000/api/query/getUser?userId=123 ``` **POST Request:** ```bash curl -X POST http://localhost:5000/api/query/getUser \ -H "Content-Type: application/json" \ -d '{"userId": 123}' ``` **Response:** ```json { "id": 123, "name": "John Doe", "email": "john@example.com" } ``` ## Endpoint Routing ### Default Routes ``` Commands: POST /api/command/{commandName} Queries: GET /api/query/{queryName} POST /api/query/{queryName} ``` ### Custom Route Prefix ```csharp app.MapSvrntyCommands("my-commands"); // POST /my-commands/{name} app.MapSvrntyQueries("my-queries"); // GET/POST /my-queries/{name} ``` ### Custom Command Names ```csharp [CommandName("users/create")] public record CreateUserCommand { } // Endpoint: POST /api/command/users/create ``` ## HTTP Status Codes ### Success Responses | Status | Scenario | |--------|----------| | 200 OK | Query success, Command with result | | 201 Created | Command created a resource | | 204 No Content | Command without result | ### Error Responses | Status | Scenario | |--------|----------| | 400 Bad Request | Validation failure (RFC 7807) | | 401 Unauthorized | Missing/invalid authentication | | 403 Forbidden | Authorization failure | | 404 Not Found | Entity not found | | 409 Conflict | Duplicate/constraint violation | | 500 Internal Server Error | Unhandled exception | ## Validation Errors Validation failures return RFC 7807 Problem Details: **Request:** ```bash POST /api/command/createUser {"name": "", "email": "invalid"} ``` **Response:** ```json { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "One or more validation errors occurred.", "status": 400, "errors": { "Name": ["Name is required"], "Email": ["Valid email address is required"] } } ``` ## Authorization ### Command Authorization ```csharp public class DeleteUserCommandAuthorization : ICommandAuthorizationService { public Task CanExecuteAsync( DeleteUserCommand command, ClaimsPrincipal user, CancellationToken cancellationToken) { // Only admins can delete users return Task.FromResult(user.IsInRole("Admin")); } } // Registration builder.Services.AddScoped, DeleteUserCommandAuthorization>(); ``` **Unauthorized Response:** ``` HTTP/1.1 403 Forbidden ``` ## Documentation ### [Endpoint Mapping](endpoint-mapping.md) How endpoints are generated: - Discovery process - Endpoint generation - Route patterns - HTTP methods ### [Naming Conventions](naming-conventions.md) URL naming and customization: - Default naming rules - Custom endpoint names - RESTful patterns - Versioning strategies ### [HTTP Configuration](http-configuration.md) Configuration and customization: - Route prefixes - HTTP method selection - CORS configuration - Authentication/authorization ### [Swagger Integration](swagger-integration.md) OpenAPI/Swagger setup: - Swagger UI - API documentation - Response types - Example requests ### [HTTP Troubleshooting](http-troubleshooting.md) Common issues and solutions: - 404 Not Found - 400 Bad Request - CORS errors - Serialization issues ## Complete Example ```csharp using Svrnty.CQRS; using Svrnty.CQRS.MinimalApi; using FluentValidation; var builder = WebApplication.CreateBuilder(args); // CQRS services builder.Services.AddSvrntyCQRS(); builder.Services.AddDefaultCommandDiscovery(); builder.Services.AddDefaultQueryDiscovery(); // Commands builder.Services.AddCommand(); builder.Services.AddTransient, CreateUserCommandValidator>(); // Queries builder.Services.AddQuery(); builder.Services.AddQuery, ListUsersQueryHandler>(); // Swagger builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Swagger UI if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } // CQRS endpoints app.MapSvrntyCommands(); app.MapSvrntyQueries(); app.Run(); ``` ## Best Practices ### ✅ DO - Use POST for commands (idempotent operations) - Support both GET and POST for queries - Return appropriate HTTP status codes - Use RFC 7807 for validation errors - Document endpoints with Swagger - Use authorization services for security - Handle 404 Not Found gracefully ### ❌ DON'T - Don't use GET for commands (non-idempotent) - Don't expose sensitive data in URLs (use POST) - Don't skip validation - Don't return 500 for business logic errors - Don't bypass authorization - Don't ignore content negotiation ## What's Next? - **[Endpoint Mapping](endpoint-mapping.md)** - How endpoints are generated - **[Naming Conventions](naming-conventions.md)** - URL naming and customization - **[HTTP Configuration](http-configuration.md)** - Configuration options - **[Swagger Integration](swagger-integration.md)** - API documentation - **[HTTP Troubleshooting](http-troubleshooting.md)** - Common issues ## See Also - [Commands Overview](../core-features/commands/README.md) - [Queries Overview](../core-features/queries/README.md) - [Validation Overview](../core-features/validation/README.md) - [gRPC Integration](../grpc-integration/README.md) - [Getting Started: Choosing HTTP or gRPC](../getting-started/06-choosing-http-or-grpc.md)