Defining Endpoints
The IEndpoint Interface
Section titled “The IEndpoint Interface”The IEndpoint interface is the core of AxisEndpoints. By implementing it, each endpoint keeps routing, metadata, and request handling in a single class.
Two variants
Section titled “Two variants”There are two variants depending on whether the endpoint accepts request parameters:
// Endpoint with request parameterspublic interface IEndpoint<TRequest, TResult>{ void Configure(IEndpointConfiguration config); Task<TResult> HandleAsync(TRequest request, CancellationToken cancel);}
// Endpoint without request parameterspublic interface IEndpoint<TResult>{ void Configure(IEndpointConfiguration config); Task<TResult> HandleAsync(CancellationToken cancel);}Two responsibilities
Section titled “Two responsibilities”Configure(): Declares routing (HTTP method and path), OpenAPI metadata (tags and summary), authorization policies, and filtersHandleAsync(): Processes the request and returns the response
TResult types
Section titled “TResult types”TResult is typically one of the following:
Response<TBody>: A JSON response. The 200 response schema is registered in OpenAPI automaticallyIResult: A custom response. Use this for multiple response shapes or non-JSON responses such as CSV export
Constructor injection
Section titled “Constructor injection”Endpoint classes are resolved from the DI container, so you can inject dependencies through the constructor. Assembly scanning in AddAxisEndpoints() automatically registers classes that implement IEndpoint.
Supported HTTP methods
Section titled “Supported HTTP methods”IEndpointConfiguration supports the following six HTTP methods:
| Method | Configuration method |
|---|---|
| GET | config.Get(route) |
| POST | config.Post(route) |
| PUT | config.Put(route) |
| PATCH | config.Patch(route) |
| DELETE | config.Delete(route) |
| HEAD | config.Head(route) |
JSON response — Response<TBody>
Section titled “JSON response — Response<TBody>”Use IEndpoint<TRequest, Response<TBody>> for POST, PUT, and PATCH endpoints where TRequest is bound from the JSON request body.
public class CreateUserRequest{ public required string Name { get; init; } public required string Email { get; init; }}
public class CreateUserResponse{ public required int Id { get; init; }}
public class CreateUserEndpoint : IEndpoint<CreateUserRequest, Response<CreateUserResponse>>{ private readonly IUserRepository _repository;
public CreateUserEndpoint(IUserRepository repository) => _repository = repository;
public void Configure(IEndpointConfiguration config) { config.Post("/users") .Tags("Users") .Summary("Create a new user"); }
public async Task<Response<CreateUserResponse>> HandleAsync( CreateUserRequest request, CancellationToken cancel) { var id = await _repository.CreateAsync(request.Name, request.Email, cancel);
return new Response<CreateUserResponse> { StatusCode = HttpStatusCode.Created, Headers = [("Location", $"/users/{id}")], Body = new CreateUserResponse { Id = id }, }; }}No response body — Response<EmptyResponse>
Section titled “No response body — Response<EmptyResponse>”Use Response<EmptyResponse> for endpoints that return no body. The Response.NoContent shorthand returns 204 No Content in a single expression.
public class DeleteUserEndpoint : IEndpoint<DeleteUserRequest, Response<EmptyResponse>>{ public void Configure(IEndpointConfiguration config) { config.Delete("/users/{id}").Summary("Delete a user"); }
public Task<Response<EmptyResponse>> HandleAsync( DeleteUserRequest request, CancellationToken cancel) { // delete user ... return Task.FromResult(Response.NoContent); }}Response.Empty is the 200 OK equivalent for the same no-body pattern.
No request parameters — IEndpoint<TResult>
Section titled “No request parameters — IEndpoint<TResult>”Use IEndpoint<TResult> when the endpoint takes no parameters at all. If query parameters are needed, define a request type with [FromQuery] properties and use IEndpoint<TRequest, TResult> instead.
public class HealthEndpoint : IEndpoint<Response<HealthResponse>>{ public void Configure(IEndpointConfiguration config) { config.Get("/health").AllowAnonymous(); }
public Task<Response<HealthResponse>> HandleAsync(CancellationToken cancel) { return Task.FromResult(new Response<HealthResponse> { Body = new HealthResponse { Status = "ok", Timestamp = DateTimeOffset.UtcNow }, }); }}For details on how response types work, see the Response Types guide.