Skip to main content

RA.Utilities.OpenApi

NuGet version NuGet Downloads Codecov Publish NuGet GitHub license

RA.Utilities.OpenApi provides a collection of document, operation, and schema transformers that automate common OpenAPI specification modifications. Instead of manually annotating every endpoint, apply transformations globally — add security schemes, inject headers, enrich schemas from FluentValidation rules, and document enum values from XML comments.

🎯 Purpose​

The built-in Microsoft.AspNetCore.OpenApi package generates a basic OpenAPI document. Making it production-ready — adding Bearer auth, correlation headers, validation constraints, enum descriptions, default error responses — typically requires dozens of lines of boilerplate per endpoint.

This package collapses all of that into a few extension methods:

builder.Services.AddOpenApi(options =>
{
options.AddDefaultsDocumentTransformer();
options.AddFluentValidationRules();
options.AddDefaultResponsesOperationTransformer();
});

The primary goals are:

  • Reduce Boilerplate: Automatically add common parameters (correlation IDs, security schemes) to all API operations.
  • Enforce Consistency: Ensure all endpoints consistently document required headers, error responses, and validation constraints.
  • Simplify Configuration: Keep endpoint definitions clean by centralizing OpenAPI modifications in Program.cs and appsettings.json.

✨ Key Features​

  • Document Transformers — 7 transformers: DocumentInfo, BearerSecurity, HeadersParameter, HealthChecks, Polymorphism, TagOperation, GroupTags, plus a combined AddDefaultsDocumentTransformer() convenience method
  • Group-level documentation — WithGroupSummary() / WithGroupDescription() document a whole route group once, and GroupTagsDocumentTransformer writes that text into the group's OpenAPI tag
  • Operation Transformers — DefaultResponsesOperationTransformer adds standardized 500 error responses
  • Schema Transformers — FluentValidationSchemaTransformer enriches schemas from validators, ValueObjectSchemaTransformer documents the constraints of the value objects, EnumXmlSchemaTransformer appends XML doc descriptions
  • Configuration-driven — OpenApiInfoSettings and HeadersParameterSettings bind from appsettings.json
  • Utility helpers — OpenApiOperationUtilities for programmatically adding request/response examples

🛠️ Getting Started​

Prerequisites​

  • .NET 10.0 or later
  • An ASP.NET Core application with Microsoft.AspNetCore.OpenApi configured

Installation​

.NET CLI

dotnet add package RA.Utilities.OpenApi

Package Manager Console

Install-Package RA.Utilities.OpenApi

PackageReference (csproj)

<PackageReference Include="RA.Utilities.OpenApi" Version="10.3.0" />

Hello World​

Minimal setup with the default transformers:

using RA.Utilities.OpenApi.Extensions;
using RA.Utilities.OpenApi.Settings;

var builder = WebApplication.CreateBuilder(args);

// Bind settings from appsettings.json
builder.Services.Configure<OpenApiInfoSettings>(
builder.Configuration.GetSection(OpenApiInfoSettings.AppSettingsKey));
builder.Services.Configure<HeadersParameterSettings>(
builder.Configuration.GetSection(HeadersParameterSettings.AppSettingsKey));

builder.Services.AddOpenApi(options =>
{
options.AddDefaultsDocumentTransformer();
options.AddFluentValidationRules();
options.AddDefaultResponsesOperationTransformer();
});

var app = builder.Build();

app.MapOpenApi();
app.Run();

🧠 Core Concepts​

Transformer types​

The package provides three kinds of transformers, matching the ASP.NET Core OpenAPI extension points:

TypeInterfaceApplied toExamples
Document TransformerIOpenApiDocumentTransformerEntire OpenAPI documentInfo population, security schemes, header injection, polymorphism, group tag descriptions
Operation TransformerIOpenApiOperationTransformerIndividual API operationsDefault error responses
Schema TransformerIOpenApiSchemaTransformerIndividual schema objectsFluentValidation rules, value object constraints, enum descriptions

Configuration flow​

appsettings.json Program.cs
──────────────── ──────────
OpenApiInfoSettings ────────────► DocumentInfoTransformer
(Title, Version, etc.) populates document.Info

OpenApiHeaders ────────────► HeadersParameterTransformer
(RequestHeaders, adds headers to all ops
ResponseHeaders)

(FluentValidation ────────────► FluentValidationSchemaTransformer
validators in DI) enriches schema properties

(RA.Utilities.Core ────────────► ValueObjectSchemaTransformer
value objects) adds types, patterns, and lengths

(XML doc comments) ────────────► EnumXmlSchemaTransformer
appends enum value tables

Registration​

All transformers are internal sealed — register them through the extension methods in RA.Utilities.OpenApi.Extensions using the AddOpenApi(options => ...) overload:

v10.1.1 rename

In v10.1.1 the extension class was renamed from DependencyInjectionExtensions to OpenApiOptionsExtensions, and its methods are now declared as C# 14 extension members on OpenApiOptions. Method names, signatures, and call sites are unchanged — only code that references the class by name needs updating.

v10.1.1 dependency

This release references RA.Utilities.Api.Response 10.1.0, which moved its response models from RA.Utilities.Api.Results to the RA.Utilities.Api.Response namespace. The OpenApi public API is unchanged; update using directives only if your custom transformers or examples reference those models directly.

v10.2.0 dependency

This release adds a dependency on RA.Utilities.Core.ValueObjects 10.2.0 for the value object schema definitions. AddDefaultsDocumentTransformer() registers the new ValueObjectSchemaTransformer automatically.

MethodRegisters
AddDefaultsDocumentTransformer()DocumentInfo + BearerSecurity + HeadersParameter + HealthChecks + GroupTags document transformers, plus the ValueObjectSchemaTransformer
AddFluentValidationRules()FluentValidationSchemaTransformer
AddValueObjectSchemaTransformer()ValueObjectSchemaTransformer
AddEnumXmlDescriptionTransformer(path)EnumXmlSchemaTransformer
AddDefaultResponsesOperationTransformer()DefaultResponsesOperationTransformer
AddDocumentInfoTransformer()DocumentInfoTransformer individually
AddBearerSecurityDocumentTransformer()BearerSecurityDocumentTransformer individually
AddHeadersParameterTransformer()HeadersParameterTransformer individually
AddPolymorphismDocumentTransformer<T>(types, discriminatorPropertyName = "Type")PolymorphismDocumentTransformer
AddTagOperationTransformer(tags)TagOperationTransformer
AddHealthChecksDocumentTransformer(tags)⚠️ Currently registers HeadersParameterTransformer instead of HealthChecksDocumentTransformer — see the caution below
AddGroupTagsDocumentTransformer(tags)GroupTagsDocumentTransformer; the tags argument is currently ignored — see the caution below
v10.1.0, still present in v10.3.0

AddHealthChecksDocumentTransformer(tags) currently registers HeadersParameterTransformer instead of HealthChecksDocumentTransformer, and the tags argument is ignored. Use AddDefaultsDocumentTransformer() to document health check endpoints until this is corrected.

v10.3.0

AddGroupTagsDocumentTransformer(tags) requires a tags argument but ignores it. Group documentation is read from the GroupOpenApiMetadata declared by WithGroupSummary() / WithGroupDescription() on the route group, not from this dictionary. AddDefaultsDocumentTransformer() already registers the transformer.

Group-level documentation​

WithGroupSummary() and WithGroupDescription() are route-group conventions, not endpoint annotations. They store GroupOpenApiMetadata in the metadata of every endpoint in the group (nested groups inherit it), and GroupTagsDocumentTransformer projects the composed text onto the OpenAPI tags carried by those endpoints. This documents a group once instead of repeating WithSummary/WithDescription on every operation.


📖 How-To Guides​

Populate API info from configuration​

Add to appsettings.json:

{
"OpenApiInfoSettings": {
"Title": "My API",
"Version": "v1",
"Description": "API for managing products and orders."
}
}

Bind and register:

builder.Services.Configure<OpenApiInfoSettings>(
builder.Configuration.GetSection(OpenApiInfoSettings.AppSettingsKey));

builder.Services.AddOpenApi(options =>
{
options.AddDocumentInfoTransformer();
});

Add Bearer token security to Swagger UI​

builder.Services.AddOpenApi(options =>
{
options.AddBearerSecurityDocumentTransformer();
});

When JWT Bearer authentication is registered in DI, this adds the security scheme and applies it to all operations, enabling the "Authorize" button in Swagger UI.

Add correlation headers to all endpoints​

builder.Services.Configure<HeadersParameterSettings>(
builder.Configuration.GetSection(HeadersParameterSettings.AppSettingsKey));

builder.Services.AddOpenApi(options =>
{
options.AddHeadersParameterTransformer();
});

With default settings, this adds x-request-id to requests and x-request-id + trace-id to responses. Customize via appsettings.json:

{
"OpenApiHeaders": {
"RequestHeaders": [
{
"Name": "x-api-key",
"Description": "API key for authentication",
"Required": true
}
]
}
}

Enrich schemas from FluentValidation rules​

builder.Services.AddOpenApi(options =>
{
options.AddFluentValidationRules();
});

Requires FluentValidation validators to be registered in DI. Supported rules: NotNull/NotEmpty (required), Length (minLength/maxLength), Matches (pattern), GreaterThan/LessThan (minimum/maximum), InclusiveBetween/ExclusiveBetween, EmailAddress, CreditCard.

Document value object constraints​

builder.Services.AddOpenApi(options =>
{
options.AddValueObjectSchemaTransformer();
});

Adds the type, format, pattern, minLength, and maxLength enforced by Email, Currency, SSN, and IBAN, plus a description and example to Money. AddDefaultsDocumentTransformer() registers this transformer automatically.

Add enum value descriptions from XML comments​

var xmlPath = Path.Combine(AppContext.BaseDirectory, "MyApi.xml");

builder.Services.AddOpenApi(options =>
{
options.AddEnumXmlDescriptionTransformer(xmlPath);
});

Produces a Markdown table in the schema description:

ValueDescription
PendingOrder has been placed but not processed.
ShippedOrder has been dispatched to the customer.

Add default 500 error response to all operations​

builder.Services.AddOpenApi(options =>
{
options.AddDefaultResponsesOperationTransformer();
});

Support polymorphic types in the schema​

builder.Services.AddOpenApi(options =>
{
options.AddPolymorphismDocumentTransformer<Animal>(new Dictionary<string, Type>
{
{ "Cat", typeof(Cat) },
{ "Dog", typeof(Dog) },
}, discriminatorPropertyName: "animalType");
});

Add tag descriptions​

builder.Services.AddOpenApi(options =>
{
options.AddTagOperationTransformer(new Dictionary<string, string>
{
{ "Products", "Product catalog and inventory operations." },
{ "Orders", "Order placement and fulfillment operations." }
});
});

Document a route group once​

WithGroupSummary() and WithGroupDescription() attach documentation to a route group. Every endpoint in the group (including nested groups) inherits it, and GroupTagsDocumentTransformer writes it into each tag the group's endpoints carry.

using RA.Utilities.OpenApi.Extensions;

var orders = app.MapGroup("/orders")
.WithTags("Orders") // the tag that receives the description
.WithGroupSummary("Order management") // rendered as the bold heading
.WithGroupDescription("Create, update, and track customer orders.");

orders.MapGet("/{id:int}", (int id) => Results.Ok(new { Id = id }));

The tag description is composed as **Summary** + a blank line + the description when both are set, or as the summary/description alone when only one is set. Group text is only written to tags the endpoints actually carry (add .WithTags(...) to the group), and it never replaces a tag description that is already present — for example one configured with AddTagOperationTransformer().

AddDefaultsDocumentTransformer() registers the transformer automatically. When registering transformers individually, call options.AddGroupTagsDocumentTransformer(...); see RouteGroupBuilderExtensions and GroupTagsDocumentTransformer.

Document health check endpoints​

builder.Services.AddOpenApi(options =>
{
options.AddDefaultsDocumentTransformer();
});

Health check endpoints mapped with MapHealthChecks carry no API metadata, so the OpenAPI generator never includes them. The default pipeline now documents GET /health (the aggregate JSON health report) and GET /alive (the plain-text liveness state) under the Health tag, without applying the Bearer security scheme — the endpoints stay reachable without authentication.

Add custom request/response examples programmatically​

using RA.Utilities.OpenApi.Utilities;

// In a custom transformer
OpenApiOperationUtilities.AddRequestExample(operation, "CreateProduct",
new OpenApiExample
{
Summary = "A new product",
Value = JsonSerializer.SerializeToNode(new { Name = "Widget", Price = 9.99 })
});

OpenApiOperationUtilities.AddResponseExample(operation, 200, "Success",
new OpenApiExample
{
Summary = "Product created",
Value = JsonSerializer.SerializeToNode(new { Id = 1, Name = "Widget" })
});

⚙️ Configuration​

OpenApiInfoSettings​

Section: OpenApiInfoSettings

PropertyTypeDescription
UiReferenceEndpointstring?The endpoint where the Scalar API reference UI is mapped. Defaults to "/".
Titlestring?The title of the API.
Versionstring?The version of the API.
Descriptionstring?A short description.
Summarystring?A brief summary.
TermsOfServiceUri?URL to Terms of Service.
ContactOpenApiContactSettings?Contact info (Name, Email, Url).
LicenseOpenApiLicenseSettings?License info (Name, Url, Identifier).

HeadersParameterSettings​

Section: OpenApiHeaders

PropertyTypeDescription
RequestHeadersList<HeaderDefinition>Headers added to all requests.
ResponseHeadersList<HeaderDefinition>Headers added to all responses.

HeaderDefinition:

PropertyTypeDefaultDescription
Namestring""Header name (e.g., "x-request-id").
Descriptionstring""Description of the header.
RequiredbooltrueWhether the header is required.
TypeJsonSchemaTypeStringSchema type.
Formatstring?nullFormat (e.g., "uuid").
Valueobject?nullExample value.

✨ Available Components​

Document Transformers​

Extensions​

The RA.Utilities.OpenApi.Extensions namespace provides all registration extension methods on OpenApiOptions. See the Extensions page for details.

Utilities​

The OpenApiOperationUtilities static class provides helpers for adding examples to operations. See the Utilities page.


🙌 Contributing & Support​

Found a bug or have a suggestion?​

Contributing​

Contributions are welcome! Please follow the standard workflow:

  1. Fork the RA.Utilities repository
  2. Create a branch from main with a descriptive name
  3. Make your changes — new transformers must implement the appropriate IOpenApi*Transformer interface and have an accompanying extension method
  4. Submit a pull request to the main branch with a clear description
ResourceURL
NuGetRA.Utilities.OpenApi
SourceGitHub — RedonAlla/RA.Utilities
LicenseMIT
Build StatusGitHub Actions