Skip to content

REST API — Attribute Reference

[RestApi]

Marks a [Runtime] class as a REST API entry point. The generator discovers all dispatch handlers with HTTP method attributes and produces Minimal API endpoint mappings.

[Runtime]
[RestApi(RoutePrefix = "/api/catalog", Title = "eShop Catalog API")]
public partial class CatalogApp;

Target: partial class

Properties:

Property Type Default Description
RoutePrefix string "/api" Base route prefix for all generated endpoints
Title string? Inferred from assembly Application title for OpenAPI metadata

Constraints:

  • One [RestApi] per class
  • The class must be partial
  • Generates Add{TypeName}() and Map{TypeName}() extension methods

HTTP Method Attributes

These attributes map dispatch handlers to HTTP endpoints. Apply them alongside [CommandHandler] or [QueryHandler].

[HttpGet(string template)]

public sealed record GetCatalogItemById(CatalogItemId Id) : IQuery;

public static class GetCatalogItemByIdHandler
{
    [QueryHandler, HttpGet("/items/{id}")]
    public static Eff<CatalogRuntime, Option<CatalogItem>> Handle(GetCatalogItemById query) =>
        CatalogStore.CatalogItems.GetById<CatalogRuntime>(query.Id);
}

Route parameters bind from the URL path. Query string parameters are also supported via record properties.

[HttpPost(string template)]

public sealed record CreateCatalogItem(string Name, decimal Price) : ICommand;

public static class CreateCatalogItemHandler
{
    [CommandHandler, HttpPost("/items")]
    public static Eff<CatalogRuntime, CatalogItem> Handle(CreateCatalogItem cmd) =>
        from item in SuccessEff(new CatalogItem(CatalogItemId.New(), cmd.Name, cmd.Price))
        from _ in CatalogStore.CatalogItems.Save<CatalogRuntime>(item)
        select item;
}

The request body is deserialized as the command record. Returns 201 Created by default.

[HttpPut(string template)]

public sealed record UpdateCatalogItemPrice(CatalogItemId ItemId, decimal NewPrice) : ICommand;

public static class UpdateCatalogItemPriceHandler
{
    [CommandHandler, HttpPut("/items/{catalogItemId}/price")]
    public static Eff<CatalogRuntime, ProductPriceChanged> Handle(UpdateCatalogItemPrice cmd) =>
        from item in CatalogStore.CatalogItems.Require<CatalogRuntime>(cmd.ItemId)
        let oldPrice = item.Price
        from _ in Mutate<CatalogRuntime>(() => item.Price = cmd.NewPrice)
                  >> CatalogStore.CatalogItems.Save<CatalogRuntime>(item)
        select new ProductPriceChanged(cmd.ItemId, oldPrice, cmd.NewPrice);
}

Full resource replacement. Returns 204 No Content.

[HttpPatch(string template)]

public sealed record UpdateCatalogItemDescription(
    CatalogItemId ItemId, string Description) : ICommand;

public static class UpdateCatalogItemDescriptionHandler
{
    [CommandHandler, HttpPatch("/items/{catalogItemId}/description")]
    public static Eff<CatalogRuntime, Unit> Handle(UpdateCatalogItemDescription cmd) =>
        from item in CatalogStore.CatalogItems.Require<CatalogRuntime>(cmd.ItemId)
        from _ in Mutate<CatalogRuntime>(() => item.Description = cmd.Description)
                  >> CatalogStore.CatalogItems.Save<CatalogRuntime>(item)
        select unit;
}

Partial resource update. Returns 200 OK.

[HttpDelete(string template)]

public sealed record DeleteCatalogItem(CatalogItemId ItemId) : ICommand;

public static class DeleteCatalogItemHandler
{
    [CommandHandler, HttpDelete("/items/{catalogItemId}")]
    public static Eff<CatalogRuntime, Unit> Handle(DeleteCatalogItem cmd) =>
        CatalogStore.CatalogItems.Delete<CatalogRuntime>(cmd.ItemId);
}

Returns 204 No Content by default (no response body).

Common Properties

All HTTP method attributes inherit from HttpMethodAttribute:

Property Type Description
Template string The route template (e.g., "/api/items/{id}")

Route parameters use ASP.NET Core conventions: {id}, {id:guid}, etc.


[Public]

Marks a handler as publicly accessible — no authentication required, even when [WebAuth] is configured on the runtime.

public sealed record GetCatalogItems(int Page = 1, int PageSize = 10) : IQuery;

public static class GetCatalogItemsHandler
{
    [QueryHandler, HttpGet("/items"), Public]
    public static Eff<CatalogRuntime, QueryResult<CatalogItem>> Handle(GetCatalogItems query) =>
        CatalogStore.CatalogItems.QueryPage<CatalogRuntime>(query.Page, query.PageSize);
}

Without [Public], all endpoints require a valid JWT Bearer token when [WebAuth] is present.


[WebAuth(params string[] providers)]

Enables authentication endpoint generation on a [RestApi] runtime. Each provider gets its own login endpoint.

[RestApi(RoutePrefix = "/api", Title = "eShop")]
[WebAuth("google")]
public partial class AppApi;

Generated endpoints:

Endpoint Method Description
/api/auth/{provider}/login POST Verify provider token, issue JWT access + refresh tokens
/api/auth/refresh POST Exchange refresh token for new access token
/api/auth/logout POST Revoke refresh token

Supported providers: "google" (via IGoogleTokenVerifier). Additional providers can be added by implementing the auth interface pattern.

Requires: Deepstaging.Auth package for IJwtService, IGoogleTokenVerifier, and HttpContextUserContext implementations.


[WebSocket(WebSocketProvider provider)]

Enables WebSocket support on a [RestApi] runtime. Generates a WebSocket upgrade endpoint.

[RestApi(RoutePrefix = "/api", Title = "eShop")]
[WebSocket(WebSocketProvider.InMemory)]
public partial class AppApi;

Generated endpoint:

Endpoint Description
/ws WebSocket upgrade with JWT authentication from ?token= query parameter

Providers:

Value Description
WebSocketProvider.InMemory Single-server ConcurrentDictionary-backed connection manager

See WebSocket for the connection manager API.


[AzureFunctionsApi]

Marks a partial class as the Azure Functions Isolated Worker API entry point. The generator produces [Function] classes with [HttpTrigger] bindings for all dispatch handlers that have HTTP route attributes.

[AzureFunctionsApi]
public partial class Startup;

Target: partial class

Properties:

Property Type Default Description
RoutePrefix string "" Base route prefix for all generated function endpoints

Notes:

  • Can coexist with [RestApi] on the same class — both generators emit independently
  • Does not use group prefixes like Minimal APIs — Azure Functions routing applies its own conventions
Program.cs
var host = new HostBuilder()
    .ConfigureFunctionsWebApplication()
    .AddAppRuntime()
    .Build();
host.Run();

[Webhook<TValidator>]

Marks a handler method as a webhook endpoint requiring signature validation before processing. Apply alongside an HTTP verb attribute (typically [HttpPost]).

[CommandHandler]
[HttpPost("/webhooks/twilio/sms")]
[Webhook<TwilioWebhookValidator>]
public static Eff<AppRuntime, Unit> HandleInboundSms(AcceptReply cmd) => ...

Type parameter: TValidator must implement IWebhookValidator — the runtime calls its ValidateAsync method to verify the inbound request signature before the handler executes.

Usage: Webhook validators are typically provided by infrastructure packages (e.g., TwilioWebhookValidator from Deepstaging.Twilio). The generated endpoint reads the raw request body and headers, passes them to the validator, and short-circuits with 401 Unauthorized on failure.