PouryaKit
Guides

Add an endpoint

Serve a Feature over HTTP with an endpoint beside it that sends its command or query through the Module's Gateway and answers its Result.

What you build

One internal sealed class implementing IEndpoint beside its Feature. Its Map maps the Feature's route on its Module's route group. It injects the request's OperationContext and the Module's Gateway, sends the command or query, and answers with ToHttpResult(). It holds no business logic and touches no repository.

Authenticated by default

Every endpoint refuses an unauthenticated caller unless it says otherwise. A public endpoint names the system Actor its anonymous callers run as, with AllowAnonymousAs. A tenant-scoped Module maps no public endpoint: an anonymous caller has no tenant.

When to use it

For each Feature a client calls over HTTP. A Feature only other Modules use needs no endpoint: they reach it through the Module's Contracts.

Steps

Write the endpoint beside its Feature

Name it after the Feature with the Endpoint suffix. Inventory's GetStockItem endpoint sends its query through Inventory's Gateway with the context the request began:

GetStockItemEndpoint.cs
// No :guid route constraint: an id that does not parse is the caller's mistake (400), not a missing route (404).
internal sealed class GetStockItemEndpoint : IEndpoint
{
    public void Map(IEndpointRouteBuilder routes) =>
        routes.MapGet(
            "/stock-items/{productId}",
            async (
                Guid productId,
                OperationContext context,
                IGatewayProcessor<InventoryModule> inventory,
                CancellationToken cancellationToken
            ) =>
                (
                    await inventory.QueryAsync(
                        new GetStockItemQuery(productId),
                        context,
                        cancellationToken
                    )
                ).ToHttpResult()
        );
}

For a body, declare an internal sealed record beside the endpoint and map it to the command by hand.

Choose the success shape

ToHttpResult() answers a success with or without the value, as the command or query has one; its overload that takes a mapping answers another shape, such as a created resource with its location. Every failure answers one problem response, whichever overload you use. From Result to response gives each status.

Declare the Module's endpoints

The Module's Composition Root calls AddPouryaKitEndpoints with the Module's route prefix, as Inventory's does, and finds every endpoint in the Module's assembly. The Host maps every Module's endpoints with MapPouryaKitModuleEndpoints().

Call it as an authenticated caller

The Web Capability begins the request's Operation with a context built from the authenticated caller: the Actor and, where the Host configures one, the tenant, each from the claim the Host names for it; the Kit's JWT bearer scheme writes both from a verified token. A test calls it with Fixture.Host.GetTestClient(): see Testing over HTTP.

The monolith template has no Gateway: its endpoints send through IDispatcher instead. See What each composition composes.

What the Kit guarantees

GuaranteeStated in
How a Result becomes a responseFrom Result to response
One problem response shape for every failureProblem responses
Authenticated access by default, and a public endpoint's ActorThe caller and the Actor
An endpoint that would never answer stops the HostThe Host

Failure messages

Next steps

On this page