RA.Utilities.OpenApi
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.csandappsettings.json.
✨ Key Features
- Document Transformers — 7 transformers:
DocumentInfo,BearerSecurity,HeadersParameter,HealthChecks,Polymorphism,TagOperation,GroupTags, plus a combinedAddDefaultsDocumentTransformer()convenience method - Group-level documentation —
WithGroupSummary()/WithGroupDescription()document a whole route group once, andGroupTagsDocumentTransformerwrites that text into the group's OpenAPI tag - Operation Transformers —
DefaultResponsesOperationTransformeradds standardized 500 error responses - Schema Transformers —
FluentValidationSchemaTransformerenriches schemas from validators,ValueObjectSchemaTransformerdocuments the constraints of the value objects,EnumXmlSchemaTransformerappends XML doc descriptions - Configuration-driven —
OpenApiInfoSettingsandHeadersParameterSettingsbind fromappsettings.json - Utility helpers —
OpenApiOperationUtilitiesfor programmatically adding request/response examples
🛠️ Getting Started
Prerequisites
- .NET 10.0 or later
- An ASP.NET Core application with
Microsoft.AspNetCore.OpenApiconfigured
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:
| Type | Interface | Applied to | Examples |
|---|---|---|---|
| Document Transformer | IOpenApiDocumentTransformer | Entire OpenAPI document | Info population, security schemes, header injection, polymorphism, group tag descriptions |
| Operation Transformer | IOpenApiOperationTransformer | Individual API operations | Default error responses |
| Schema Transformer | IOpenApiSchemaTransformer | Individual schema objects | FluentValidation 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:
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.
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.
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.
| Method | Registers |
|---|---|
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 |
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.
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:
| Value | Description |
|---|---|
| Pending | Order has been placed but not processed. |
| Shipped | Order 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
| Property | Type | Description |
|---|---|---|
| UiReferenceEndpoint | string? | The endpoint where the Scalar API reference UI is mapped. Defaults to "/". |
| Title | string? | The title of the API. |
| Version | string? | The version of the API. |
| Description | string? | A short description. |
| Summary | string? | A brief summary. |
| TermsOfService | Uri? | URL to Terms of Service. |
| Contact | OpenApiContactSettings? | Contact info (Name, Email, Url). |
| License | OpenApiLicenseSettings? | License info (Name, Url, Identifier). |
HeadersParameterSettings
Section: OpenApiHeaders
| Property | Type | Description |
|---|---|---|
| RequestHeaders | List<HeaderDefinition> | Headers added to all requests. |
| ResponseHeaders | List<HeaderDefinition> | Headers added to all responses. |
HeaderDefinition:
| Property | Type | Default | Description |
|---|---|---|---|
| Name | string | "" | Header name (e.g., "x-request-id"). |
| Description | string | "" | Description of the header. |
| Required | bool | true | Whether the header is required. |
| Type | JsonSchemaType | String | Schema type. |
| Format | string? | null | Format (e.g., "uuid"). |
| Value | object? | null | Example value. |
✨ Available Components
Document Transformers
Document Transformers
6 items
Operation Transformers
2 items
Schema Transformers
3 items
Settings
2 items
Extensions
2 items
Utilities
1 item
Troubleshooting & FAQ
Common Issues
Migration Guides
v10.2.1 → v10.3.0
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?
- GitHub Issues: Report a bug or request a feature
- Discussions: Ask questions or start a discussion
Contributing
Contributions are welcome! Please follow the standard workflow:
- Fork the RA.Utilities repository
- Create a branch from
mainwith a descriptive name - Make your changes — new transformers must implement the appropriate
IOpenApi*Transformerinterface and have an accompanying extension method - Submit a pull request to the
mainbranch with a clear description
Links
| Resource | URL |
|---|---|
| NuGet | RA.Utilities.OpenApi |
| Source | GitHub — RedonAlla/RA.Utilities |
| License | MIT |
| Build Status | GitHub Actions |