Nabs.Launchpad.Core.Apis
10.0.287
Prefix Reserved
dotnet add package Nabs.Launchpad.Core.Apis --version 10.0.287
NuGet\Install-Package Nabs.Launchpad.Core.Apis -Version 10.0.287
<PackageReference Include="Nabs.Launchpad.Core.Apis" Version="10.0.287" />
<PackageVersion Include="Nabs.Launchpad.Core.Apis" Version="10.0.287" />
<PackageReference Include="Nabs.Launchpad.Core.Apis" />
paket add Nabs.Launchpad.Core.Apis --version 10.0.287
#r "nuget: Nabs.Launchpad.Core.Apis, 10.0.287"
#:package Nabs.Launchpad.Core.Apis@10.0.287
#addin nuget:?package=Nabs.Launchpad.Core.Apis&version=10.0.287
#tool nuget:?package=Nabs.Launchpad.Core.Apis&version=10.0.287
Nabs.Launchpad.Core.Apis
Nabs.Launchpad.Core.Apis gives you granular endpoint abstractions for consistent ASP.NET Core API endpoints.
Installation
dotnet add package Nabs.Launchpad.Core.Apis
Optional MSBuild OpenAPI export + Kiota client generation:
dotnet add package Nabs.Launchpad.Core.Apis.OpenApi.Export
The Problem
// 1) Controllers accumulate many actions over time
public class ProductsController : ControllerBase
{
[HttpGet("{id:guid}")] public IActionResult Get(Guid id) => Ok();
[HttpPost] public IActionResult Create(ProductRequest request) => Ok();
[HttpPut("{id:guid}")] public IActionResult Update(Guid id, ProductRequest request) => Ok();
}
// 2) Endpoint intent is harder to isolate by file
// 3) Growth increases merge conflicts and maintenance overhead
Features
- Single-file endpoint abstraction through
INabsEndpoint - Verb convenience bases (
NabsGetEndpoint,NabsPostEndpoint,NabsPutEndpoint,NabsPatchEndpoint,NabsDeleteEndpoint,NabsHeadEndpoint,NabsOptionsEndpoint,NabsTraceEndpoint,NabsQueryEndpoint,NabsSelectEndpoint,NabsConnectEndpoint) — route + optional configure; verb is implied - Declarative endpoint base class through
NabsEndpointBasewhen you need custom or multi-method options - Typed request/response endpoint variants for common API patterns
- FluentValidation via options (
RequestValidator/ResponseValidator) withValidateRequestAsyncas an escape hatch - Constructor injection support for discovered endpoint classes
- Automatic endpoint registration through
MapNabsEndpoints(...) - Deterministic endpoint discovery for predictable startup behavior
- Optional host helpers (
AddNabsApis/UseNabsApis) for OpenAPI + JWT bearer - Host OpenAPI self-export through
TryExportOpenApiAndExitAsync("--export-openapi") - Optional shared controller base through
NabsControllerBase
Preferred pattern
- Derive from a verb convenience base (
NabsGetEndpoint<TResponse>,NabsPostEndpoint<TRequest, TResponse>, …). - Pass route + optional configure (
: base("/api/...", static o => { ... })). The HTTP verb is implied — do not restateNabsEndpointOptions.Get/Post/...on convenience bases. Those factories are for rawNabsEndpointBaseonly. - Put metadata and validators on
NabsEndpointOptionsin the configure callback. - Register with
app.MapNabsEndpoints<Program>();
public sealed class GetProductEndpoint : NabsGetEndpoint<ProductResponse>
{
public GetProductEndpoint()
: base("/api/products/{id:guid}", static o =>
{
o.EndpointName = "GetProduct";
o.Tags = ["Products"];
o.Summary = "Get a product by id.";
})
{
}
protected override Task<ProductResponse> HandleAsync(CancellationToken cancellationToken)
=> Task.FromResult(new ProductResponse(Guid.NewGuid()));
}
public sealed class CreateProductValidator : AbstractValidator<CreateProductRequest>
{
public CreateProductValidator()
{
RuleFor(x => x.Name).NotEmpty();
}
}
public sealed class CreateProductEndpoint : NabsPostEndpoint<CreateProductRequest, CreateProductResponse>
{
public CreateProductEndpoint()
: base("/api/products", static o =>
{
o.EndpointName = "CreateProduct";
o.Tags = ["Products"];
o.RequestValidator = new CreateProductValidator();
})
{
}
protected override Task<CreateProductResponse> HandleAsync(
CreateProductRequest request, CancellationToken cancellationToken)
=> Task.FromResult(new CreateProductResponse(Guid.NewGuid(), request.Name));
}
// Program.cs
app.MapNabsEndpoints<Program>();
Put endpoint metadata on NabsEndpointOptions. Do not rely on overriding EndpointName / Tags / Summary / Description on the base for new code. Those overrides remain for compatibility only.
Prefer FluentValidation via options. Set RequestValidator / ResponseValidator in the configure callback. Do not override ValidateRequestAsync for new code (escape hatch only). Request failures map to HTTP 400 ValidationProblem; response failures map to HTTP 500 Problem (errors extension).
You can still implement INabsEndpoint directly, or use NabsEndpointBase / NabsEndpointBase<TRequest, TResponse> with NabsNone when you need custom HTTP methods or multi-method options.
public sealed class SecureProductEndpoint : NabsEndpointBase
{
private readonly IProductService _productService;
public SecureProductEndpoint(IProductService productService)
: base(NabsEndpointOptions.Get("/api/secure-products/{id:guid}", static o =>
{
o.RequireAuthorization = true;
}))
{
_productService = productService;
}
protected override Task<IResult> HandleAsync(HttpContext httpContext, CancellationToken cancellationToken)
{
var id = httpContext.GetRouteValue("id");
return Task.FromResult<IResult>(Results.Ok(_productService.GetById(id)));
}
}
public sealed class ProcessOrderEndpoint : NabsEndpointBase<ProcessOrderRequest, NabsNone>
{
public ProcessOrderEndpoint()
: base("/api/orders/process", HttpMethod.Post)
{
}
protected override Task<NabsNone> HandleAsync(ProcessOrderRequest request, CancellationToken cancellationToken)
{
return Task.FromResult(default(NabsNone));
}
}
public sealed class ValidatedOrderEndpoint : NabsEndpointBase<CreateOrderRequest, CreateOrderResponse>
{
public ValidatedOrderEndpoint()
: base("/api/orders/validated", HttpMethod.Post)
{
}
protected override Task<IDictionary<string, string[]>?> ValidateRequestAsync(CreateOrderRequest request, CancellationToken cancellationToken)
{
if (!string.IsNullOrWhiteSpace(request.CustomerName))
{
return Task.FromResult<IDictionary<string, string[]>?>(null);
}
IDictionary<string, string[]> errors = new Dictionary<string, string[]>
{
["customerName"] = ["Customer name is required."]
};
return Task.FromResult<IDictionary<string, string[]>?>(errors);
}
protected override Task<CreateOrderResponse> HandleAsync(CreateOrderRequest request, CancellationToken cancellationToken)
{
return Task.FromResult(new CreateOrderResponse(Guid.NewGuid()));
}
}
Controller-based APIs remain supported:
[ApiController]
[Route("api/[controller]")]
public sealed class ProductsController : NabsControllerBase
{
[HttpGet("{id:guid}")]
public IActionResult Get(Guid id) => Ok(new { id });
}
Optional host helpers
AddNabsApis / UseNabsApis are additive. Existing Launchpad hosts that already call AddServiceDefaults, AddOpenApi, or FeatureManagement can keep that setup and only call MapNabsEndpoints.
builder.Services.AddNabsApis(builder.Configuration);
// optional: builder.Services.AddNabsApis(builder.Configuration, o => { o.UseJwtBearerAuthentication = false; });
var app = builder.Build();
app.UseNabsApis();
app.MapNabsEndpoints<Program>();
if (await app.TryExportOpenApiAndExitAsync(args))
{
return;
}
JWT bearer settings are read from Authentication:JwtBearer (Issuer, Audience, SigningKey) when authentication is enabled.
When both UseOpenApi and UseJwtBearerAuthentication are enabled (the default), AddNabsApis(builder.Configuration) registers an OpenAPI HTTP bearer JWT security scheme named Bearer and adds a document-level security requirement referencing that scheme.
If you explicitly opt out with UseJwtBearerAuthentication = false, the Bearer OpenAPI scheme is not added (while OpenAPI can still be enabled via UseOpenApi = true).
API contract
For Launchpad API hosts that use this package, the following is the shared contract.
OpenAPI is the source of truth
- Prefer
AddNabsApis/UseNabsApis(or equivalentAddOpenApi+MapOpenApi) so the document matches mapped endpoints. - Export with
TryExportOpenApiAndExitAsync("--export-openapi")and/orNabs.Launchpad.Core.Apis.OpenApi.Exportfor Kiota clients. - Treat breaking OpenAPI changes as breaking API changes (CI OpenAPI diff is a follow-up; see Discussion #10).
Errors use Problem Details
- Ardalis
Resultfailures map to Problem Details with status-code mappings (for exampleNotFound→404,Conflict→409). - FluentValidation request failures map to HTTP
400ValidationProblem. - Response validation failures map to HTTP
500Problem Details. - Do not return bare error strings for API failures from Nabs endpoint handlers.
Authentication (default AddNabsApis)
UseOpenApiandUseJwtBearerAuthenticationdefault to true.- Default OpenAPI includes an HTTP Bearer JWT security scheme and document-level security requirement.
- Configure
Authentication:JwtBearer(Issuer,Audience,SigningKey) when JWT is enabled. - Opt out explicitly when needed:
AddNabsApis(configuration, o => o.UseJwtBearerAuthentication = false).
Gateway vs service
- Interactive login / OIDC (Entra) belongs at Gateway (
Nabs.Launchpad.Core.Gateway). - API services validate Bearer JWT (issued or forwarded by the gateway) via
AddNabsApisor equivalent shared authentication setup. - Observability defaults live in ServiceDefaults and pair with API hosts in Aspire.
Out of scope for this package contract
URI conventions, pagination helpers, and API versioning are separate ideas:
Configuration
No package-specific configuration is required for endpoint mapping.
NabsEndpointOptions supports one or more HttpMethod values and validates them against a supported set: GET, POST, PUT, DELETE, PATCH, QUERY, SELECT, HEAD, OPTIONS, TRACE, and CONNECT. When multiple methods are configured, only GET, QUERY, SELECT, HEAD, and OPTIONS combinations are allowed. Use NabsHttpMethods.Select for the non-standard SELECT method.
If you need to treat validation feedback as non-blocking, set ReturnOkOnInvalidResult = true in NabsEndpointOptions. This causes ParseResult(...) to return 200 OK with a validationErrors payload even when the Ardalis result status is Invalid.
For successful Ardalis results, ParseResult(...) maps the status code based on the configured endpoint HTTP method: POST returns 201 Created, DELETE returns 204 No Content when no payload is returned (otherwise 200 OK), and all other supported methods return 200 OK.
Project Structure
NabsGetEndpoint.cs/NabsPostEndpoint.cs(and other verb bases) - preferred convenience basesINabsEndpoint.cs- contract for one endpoint per class/fileNabsEndpointBase.cs- declarative base class with override-driven configurationNabsEndpointBaseOfT.cs- typed base for request/response endpoints (useNabsNonefor missing request or response payloads)NabsEndpointOptions.cs- route/method + metadata (canonical) and verb factoriesNabsNone.cs- marker type representing no request or no response payloadNabsEndpointRouteBuilderExtensions.cs- endpoint discovery and mapping extensionsNabsOpenApiExportExtensions.cs---export-openapihost self-exportNabsApisServiceCollectionExtensions.cs/NabsApisApplicationBuilderExtensions.cs- optional host helpersNabsControllerBase.cs- reusable base class for controller-based APIs
License
Copyright (c) Net Advantage Business Solutions.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Ardalis.Result (>= 10.1.0)
- Ardalis.Result.AspNetCore (>= 10.1.0)
- FluentValidation (>= 12.1.1)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 10.0.12)
- Microsoft.AspNetCore.OpenApi (>= 10.0.12)
- Microsoft.FeatureManagement (>= 4.7.0)
- Microsoft.FeatureManagement.AspNetCore (>= 4.7.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Nabs.Launchpad.Core.Apis:
| Package | Downloads |
|---|---|
|
Nabs.Launchpad.Core.FeatureFlags
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.287 | 0 | 9/21/2026 |
| 10.0.286 | 45 | 9/18/2026 |
| 10.0.285 | 43 | 9/18/2026 |
| 10.0.273 | 114 | 9/6/2026 |
| 10.0.272 | 102 | 9/5/2026 |
| 10.0.270 | 99 | 9/5/2026 |
| 10.0.269 | 97 | 9/5/2026 |
| 10.0.255 | 116 | 7/26/2026 |
| 10.0.250 | 120 | 6/23/2026 |
| 10.0.249 | 121 | 6/6/2026 |
| 10.0.248 | 117 | 6/6/2026 |
| 10.0.247 | 116 | 6/6/2026 |
| 10.0.246 | 124 | 6/6/2026 |
| 10.0.242 | 111 | 6/4/2026 |
| 10.0.241 | 110 | 6/4/2026 |
| 10.0.240 | 114 | 6/4/2026 |
| 10.0.239 | 110 | 6/3/2026 |
| 10.0.234 | 110 | 5/31/2026 |
| 10.0.233 | 112 | 5/31/2026 |
| 10.0.232 | 110 | 5/30/2026 |