LowCodeHub.OpenApi 0.0.2

dotnet add package LowCodeHub.OpenApi --version 0.0.2
                    
NuGet\Install-Package LowCodeHub.OpenApi -Version 0.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LowCodeHub.OpenApi" Version="0.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LowCodeHub.OpenApi" Version="0.0.2" />
                    
Directory.Packages.props
<PackageReference Include="LowCodeHub.OpenApi" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LowCodeHub.OpenApi --version 0.0.2
                    
#r "nuget: LowCodeHub.OpenApi, 0.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package LowCodeHub.OpenApi@0.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=LowCodeHub.OpenApi&version=0.0.2
                    
Install as a Cake Addin
#tool nuget:?package=LowCodeHub.OpenApi&version=0.0.2
                    
Install as a Cake Tool

LowCodeHub.OpenApi

A composable OpenAPI configuration library for ASP.NET Core built on top of Microsoft.AspNetCore.OpenApi. Every aspect of your OpenAPI document — security, error responses, deprecation, versioning — is configured through a single fluent builder. No boilerplate, no scattered configuration.

NuGet License: MIT

Why This Library?

Feature LowCodeHub.OpenApi Raw Microsoft.AspNetCore.OpenApi
Security schemes Fluent builder — JWT, API Key, OAuth2, OIDC in one line Manual transformer per scheme
Error responses Auto-added ProblemDetails (400, 401, 403, 500) Write your own operation transformer
Operation security Automatic [AllowAnonymous] / [Authorize] detection Manual metadata inspection
Deprecation Automatic [Obsolete]deprecated: true Manual transformer
Enum schemas String enums by default Manual schema transformer
Modular monolith One-liner per module with group filtering Manual ShouldInclude per document
API versioning Built-in multi-version document generation Register each version manually
Server URLs Fluent .WithServer() with description Inline transformer

Installation

dotnet add package LowCodeHub.OpenApi

Quick Start

builder.Services.AddOpenApiDoc("my-api", api => api
    .WithTitle("My API")
    .WithVersion("v1")
    .WithSecurity(sec => sec.AddJwtBearer())
    .WithErrorResponses(err => err.UseDefaults())
    .WithOperationSecurity()
    .WithDeprecationSupport());

app.MapOpenApi();

That's it. Your OpenAPI document now has JWT Bearer security on all operations, ProblemDetails error responses (400/401/403/500), smart [AllowAnonymous] detection, and [Obsolete] deprecation support — all from a single fluent chain.


Table of Contents


Fluent Builder API

Every configuration option flows through a single OpenApiBuilder:

builder.Services.AddOpenApiDoc("api", api => api
    .WithTitle("My Service")
    .WithVersion("v2")
    .WithServer("https://api.example.com", "Production")
    .WithServer("https://staging.example.com", "Staging")
    .WithSecurity(sec => sec.AddJwtBearer())
    .WithErrorResponses(err => err.UseDefaults())
    .WithOperationSecurity()
    .WithDeprecationSupport()
    .WithEnumStringSchemas());       // on by default

The first parameter is the document name — it becomes the OpenAPI endpoint path (/openapi/{documentName}.json). You can register as many documents as you need.


Security Schemes

Configure one or more security schemes through the security builder. All schemes are added to the document's components.securitySchemes and applied as requirements on every operation.

JWT Bearer

api.WithSecurity(sec => sec.AddJwtBearer());

Registers an HTTP Bearer scheme with JWT format. Customize the scheme name or format:

api.WithSecurity(sec => sec.AddJwtBearer(schemeName: "JWT", bearerFormat: "JWT"));

API Key

api.WithSecurity(sec => sec.AddApiKey("X-API-Key", ParameterLocation.Header));

Supports Header, Query, and Cookie locations. The name defaults to "X-API-Key".

OAuth2

api.WithSecurity(sec => sec.AddOAuth2(oauth => oauth
    .WithAuthorizationCodeFlow(
        authorizationUrl: "https://auth.example.com/authorize",
        tokenUrl: "https://auth.example.com/token",
        scopes: new()
        {
            ["read"] = "Read access",
            ["write"] = "Write access"
        })
    .WithClientCredentialsFlow(
        tokenUrl: "https://auth.example.com/token")));

All four OAuth2 flows are supported:

Method Flow
WithAuthorizationCodeFlow() Authorization Code
WithClientCredentialsFlow() Client Credentials
WithImplicitFlow() Implicit
WithPasswordFlow() Resource Owner Password

OpenID Connect

api.WithSecurity(sec => sec.AddOpenIdConnect(
    "https://auth.example.com/.well-known/openid-configuration"));

Custom Schemes

Escape hatch for any scheme not covered by the built-in methods:

api.WithSecurity(sec => sec.AddCustomScheme("MyScheme", new OpenApiSecurityScheme
{
    Type = SecuritySchemeType.Http,
    Scheme = "digest"
}));

Multiple Schemes

Chain multiple schemes in a single builder — all are registered and applied:

api.WithSecurity(sec => sec
    .AddJwtBearer()
    .AddApiKey("X-API-Key"));

Auto-Detection

Automatically detect security schemes from registered ASP.NET Core authentication schemes:

api.WithSecurity(sec => sec.AutoDetect());

If a "Bearer" authentication scheme is registered in your app, it's automatically added to the OpenAPI document. Combine with explicit schemes:

api.WithSecurity(sec => sec
    .AddApiKey("X-API-Key")
    .AutoDetect());            // also picks up Bearer if registered

Operation-Level Security

api.WithOperationSecurity();

When enabled, the library inspects endpoint metadata at document generation time:

  • [AllowAnonymous] → security requirements are removed from that operation
  • All other endpoints keep the document-level security requirements

This means your public endpoints (login, health checks, etc.) won't show a lock icon in the UI, while protected endpoints do.

app.MapGet("/health", () => "ok");                                    // anonymous — no lock
app.MapGet("/orders", [Authorize] () => GetOrders());                 // secured — lock shown
app.MapGet("/public", [AllowAnonymous] () => GetPublicData());        // anonymous — no lock

Common Error Responses

Automatically add RFC 7807 ProblemDetails error responses to all operations — no need to repeat [ProducesResponseType] everywhere.

Defaults

api.WithErrorResponses(err => err.UseDefaults());

Adds these responses to every operation:

Status Description Condition
400 Bad Request Always
401 Unauthorized Authenticated endpoints only
403 Forbidden Authenticated endpoints only
500 Internal Server Error Always

Custom Error Responses

Add or remove specific status codes:

api.WithErrorResponses(err => err
    .UseDefaults()
    .Add(404, "Not Found")
    .Add(409, "Conflict")
    .Add(429, "Too Many Requests")
    .Exclude(403));

Use a custom schema reference instead of inline ProblemDetails:

api.WithErrorResponses(err => err
    .Add(422, "Validation Error", "ValidationProblemDetails"));

Authenticated-Only Responses

Control which error responses only appear on authenticated endpoints:

api.WithErrorResponses(err => err
    .Add(401, "Unauthorized")
    .Add(403, "Forbidden")
    .ForAuthenticatedOnly(401, 403));

When combined with WithOperationSecurity(), endpoints marked [AllowAnonymous] won't show 401/403 responses — keeping your docs clean and accurate.


Deprecation Support

api.WithDeprecationSupport();

Endpoints decorated with [Obsolete] are automatically marked as deprecated in the OpenAPI document:

app.MapGet("/v1/users", [Obsolete("Use /v2/users instead")] () => GetUsersV1());

Produces:

{
  "deprecated": true,
  "description": "Deprecated: Use /v2/users instead"
}

The [Obsolete] message is copied to the operation description (only if no description is already set).


Enum String Schemas

Enabled by default. All enum properties in your request/response models are rendered as string values instead of integers:

public enum OrderStatus { Pending, Processing, Shipped, Delivered }

// OpenAPI schema:
// { "type": "string", "enum": ["Pending", "Processing", "Shipped", "Delivered"] }

Nullable enums (OrderStatus?) are handled automatically with "type": ["string", "null"].

To opt out:

api.DisableEnumStringSchemas();

Server URLs

Add one or more server URLs with optional descriptions:

api.WithServer("https://api.example.com", "Production")
   .WithServer("https://staging.example.com", "Staging")
   .WithServer("https://localhost:5001", "Local Development");

These appear in the OpenAPI document's servers array and in UI server dropdowns.


Modular Monolith — Document Grouping

Generate separate OpenAPI documents per module — each module gets its own entry in the UI dropdown:

// Program.cs — register one document per module
builder.Services.AddOpenApiDoc("orders", api => api
    .WithTitle("Orders API")
    .WithGroupName("Orders")
    .WithSecurity(sec => sec.AddJwtBearer())
    .WithErrorResponses(err => err.UseDefaults()));

builder.Services.AddOpenApiDoc("inventory", api => api
    .WithTitle("Inventory API")
    .WithGroupName("Inventory")
    .WithSecurity(sec => sec.AddJwtBearer())
    .WithErrorResponses(err => err.UseDefaults()));

builder.Services.AddOpenApiDoc("identity", api => api
    .WithTitle("Identity API")
    .WithGroupName("Identity")
    .WithSecurity(sec => sec.AddJwtBearer()));

In your modules, tag endpoints with the group name:

// Orders module
app.MapGroup("/api/orders")
   .WithGroupName("Orders")
   .MapOrderEndpoints();

// Inventory module
app.MapGroup("/api/inventory")
   .WithGroupName("Inventory")
   .MapInventoryEndpoints();

Each module gets its own OpenAPI document at /openapi/{documentName}.json:

  • /openapi/orders.json — only Orders endpoints
  • /openapi/inventory.json — only Inventory endpoints
  • /openapi/identity.json — only Identity endpoints

Each module can have different security configurations, error responses, and server URLs — complete isolation.


API Versioning

Generate versioned OpenAPI documents from a single configuration:

builder.Services.AddOpenApiDoc(api => api
    .WithTitle("My API")
    .WithSecurity(sec => sec.AddJwtBearer())
    .WithErrorResponses(err => err.UseDefaults())
    .WithVersioning(v => v
        .ForVersions("v1", "v2")
        .WithTitleFormat("{0} — {1}")
        .WithServerUrl("https://api.example.com")));

This registers one OpenAPI document per version:

Document Title Group filter Endpoint
v1 My API — v1 GroupName == "v1" /openapi/v1.json
v2 My API — v2 GroupName == "v2" /openapi/v2.json

Tag your endpoints with the version group name:

var v1 = app.MapGroup("/api/v1").WithGroupName("v1");
v1.MapGet("/users", () => GetUsersV1());

var v2 = app.MapGroup("/api/v2").WithGroupName("v2");
v2.MapGet("/users", () => GetUsersV2());

Title format

The WithTitleFormat method uses string.Format with {0} = base title, {1} = version:

Format Result
"{0} {1}" (default) My API v1
"{0} — {1}" My API — v1
"{0} (Version {1})" My API (Version v1)

Server URL

WithServerUrl() overrides the server URL for all versioned documents — useful when all versions share the same base URL:

.WithVersioning(v => v
    .ForVersions("v1", "v2")
    .WithServerUrl("https://api.example.com", "Production"))

How It Works

┌─────────────────────────────────┐
│  AddOpenApiDoc("name",   │
│    api => api.WithTitle(...)    │
│      .WithSecurity(...)         │  ←  Your configuration
│      .WithErrorResponses(...)   │
│      .WithOperationSecurity()   │
│  )                              │
└───────────────┬─────────────────┘
                │
                ▼
┌─────────────────────────────────┐
│  OpenApiBuilder                 │
│  ├── LowCodeHubOpenApiOptions   │  ←  Accumulated config
│  └── Apply(OpenApiOptions)      │
└───────────────┬─────────────────┘
                │
    ┌───────────┼───────────┐
    ▼           ▼           ▼
┌─────────┐ ┌─────────┐ ┌──────────┐
│Document │ │Operation│ │ Schema   │
│Transform│ │Transform│ │Transform │
├─────────┤ ├─────────┤ ├──────────┤
│Security │ │OpSec    │ │Enum      │
│Scheme   │ │(anon    │ │String    │
│Transf.  │ │ detect) │ │Schemas   │
│         │ │         │ │          │
│Info &   │ │Deprec.  │ │          │
│Servers  │ │Transf.  │ │          │
│         │ │         │ │          │
│         │ │Error    │ │          │
│         │ │Response │ │          │
│         │ │Transf.  │ │          │
└─────────┘ └─────────┘ └──────────┘
  1. RegistrationAddOpenApiDoc() creates a builder, runs your configuration lambda, stores the options as a keyed singleton, and calls AddOpenApi() with the builder's Apply method.
  2. Document transformers run first — set document info, servers, and security schemes at the document level.
  3. Operation transformers run per-endpoint — remove security from anonymous endpoints, mark deprecated operations, and add error responses.
  4. Schema transformers run per-type — convert enums to string representations.

All transformers retrieve their configuration from LowCodeHubOpenApiOptions via keyed DI, scoped to the document name.


Requirements

  • .NET 10 or later
  • Microsoft.AspNetCore.OpenApi 10.0.3+ (included as a dependency)

License

MIT © Ahmed Abuelnour

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.2 109 5/18/2026
0.0.1 164 4/16/2026