NativeOpenApi 1.7.0
dotnet add package NativeOpenApi --version 1.7.0
NuGet\Install-Package NativeOpenApi -Version 1.7.0
<PackageReference Include="NativeOpenApi" Version="1.7.0" />
<PackageVersion Include="NativeOpenApi" Version="1.7.0" />
<PackageReference Include="NativeOpenApi" />
paket add NativeOpenApi --version 1.7.0
#r "nuget: NativeOpenApi, 1.7.0"
#:package NativeOpenApi@1.7.0
#addin nuget:?package=NativeOpenApi&version=1.7.0
#tool nuget:?package=NativeOpenApi&version=1.7.0
NativeOpenApi
OpenAPI 3.1 primitives for Native AOT .NET 10 — document loading, linting,
merging, HTML rendering, and a catalog of UX attributes consumed by
NativeLambdaRouter.SourceGenerator.OpenApi.
- Version:
1.7.0 - Target:
net10.0 - Namespace root:
Native.OpenApi - AOT: zero runtime reflection; serialization through source-generated contexts
API surface at a glance
Attributes — Native.OpenApi.Attributes
| Attribute | Target | Purpose | Wave 1 § |
|---|---|---|---|
[HideFromDocs(reason?)] |
class/struct | hide operation from generated YAML | F01 |
[Deprecated(sunset, alternative, reason)] |
class/struct | emit deprecated: true + x-sunset + x-swepay-alternative + x-swepay-deprecation-reason |
F03 |
[ApiExample(name, summary) { RequestJson, ResponseStatus, ResponseJson }] (multi-use) |
class/struct | wire named examples into examples maps |
F09 |
[ErrorCatalog(typeof(T))] |
class/struct | attach an error-code catalog to the operation | F12 |
[ErrorDefinition(code, httpStatus, userMessage, recovery) { DocUrl }] |
field (const string) |
one entry inside a catalog | F12 |
[ApiResponse(statusCode, responseType?, contentType = "application/json")] (multi-use) |
method (handler's Handle) |
document per-status responses without the .Produces<T>() fluent |
v1.6.0 |
Fluent extension — Native.OpenApi.Extensions
| Method | Effect |
|---|---|
.ExcludeFromDocs() |
route-level sibling of [HideFromDocs]; identity pass-through at runtime, compile-time marker for the generator |
Models — Native.OpenApi.Models
| Type | Role |
|---|---|
SwepayProblemDetails |
RFC 9457 superset (code, recovery, requestId); generator auto-injects the matching components.schemas entry whenever any operation serves application/problem+json without a typed body (F13) |
Rendering — Native.OpenApi.Rendering
| Type | Role |
|---|---|
OpenApiRendererOptions |
aggregate; .Default = pre-Wave-1 behaviour (no brand, no footer, no Mermaid) |
OpenApiBrandingOptions |
PrimaryColor, AccentColor, LogoUrl, FaviconUrl, FontFamily, ThemeJsonOverride |
OpenApiFooterOptions |
StatusUrl, SupportUrl, ChangelogUrl, SlaUrl, TermsUrl |
Core classes
| Class | Role |
|---|---|
OpenApiHtmlRenderer |
renders Redoc + Scalar HTML; each method has a two-arg overload (legacy) and a three-arg options overload |
OpenApiDocumentLoaderBase |
base for embedded-resource spec loading (JSON + YAML) |
OpenApiDocumentMerger |
merges partial specs into a single document |
OpenApiDocumentProvider |
orchestrates load → merge → lint |
OpenApiLinter |
validates against OpenApiLintOptions rules |
OpenApiResourceReader |
reads embedded resources from an assembly |
IGeneratedOpenApiSpec |
polymorphic access to generator output (implemented automatically when this package is referenced) |
Installation
dotnet add package NativeOpenApi
Quick reference — Wave 1 (v1.7.0)
Hide endpoints from docs — F01
[HideFromDocs("internal ops endpoint")]
public sealed record InternalHealthCommand(string Secret);
Or at the route level (handy when the same command is mapped to several paths and only one should be hidden):
using Native.OpenApi.Extensions;
routes.MapGet<ListUsersCommand, ListUsersResponse>("/v1/admin/internal/users", ...)
.ExcludeFromDocs();
Effect: the operation never appears in paths:. If every operation on a path is hidden, the path itself is dropped.
Deprecation — F03
[Deprecated(
sunset: "2026-12-31",
alternative: "POST /v2/orders",
reason: "v1 doesn't support split payments.")]
public sealed record CreateOrderV1Command(...);
Emits:
deprecated: true
x-sunset: "2026-12-31"
x-swepay-alternative: "POST /v2/orders"
x-swepay-deprecation-reason: "v1 doesn't support split payments."
Named examples — F09
[ApiExample(
name: "happy-path",
summary: "Simple order with 1 item",
RequestJson = "examples/create-order/happy.json",
ResponseStatus = 201,
ResponseJson = "examples/create-order/happy-response.json")]
[ApiExample(
name: "validation-error",
summary: "Invalid CNPJ",
ResponseStatus = 422,
ResponseJson = "examples/create-order/invalid-cnpj.json")]
public sealed record CreateOrderCommand(...);
Examples reference their JSON payload through externalValue (inline payload reading is a Wave 2 follow-up — see docs/CHANGELOG.md).
Error catalog — F12
Declare once:
public static class SwepayErrors
{
[ErrorDefinition(
code: "PAYMENT_INSUFFICIENT_FUNDS",
httpStatus: 402,
userMessage: "Saldo insuficiente no método de pagamento.",
recovery: "Tente outro método de pagamento ou adicione saldo.",
DocUrl = "https://docs.swepay.com.br/errors/PAYMENT_INSUFFICIENT_FUNDS")]
public const string PaymentInsufficientFunds = "PAYMENT_INSUFFICIENT_FUNDS";
}
Wire on commands:
[ErrorCatalog(typeof(SwepayErrors))]
public sealed record CreatePaymentCommand(...);
Emits at document root:
x-swepay-error-catalog:
- code: "PAYMENT_INSUFFICIENT_FUNDS"
httpStatus: 402
userMessage: "Saldo insuficiente no método de pagamento."
recovery: "Tente outro método de pagamento ou adicione saldo."
docUrl: "https://docs.swepay.com.br/errors/PAYMENT_INSUFFICIENT_FUNDS"
Per operation (the generator slices only codes whose httpStatus matches a declared response on that operation):
x-swepay-errors:
- "PAYMENT_INSUFFICIENT_FUNDS"
Canonical problem+json schema — F13
When any operation advertises application/problem+json without a typed body, the generator injects:
components:
schemas:
SwepayProblemDetails:
type: object
properties: { type, title, status, detail, instance, code, recovery, requestId }
required: [type, title, status, detail, code, recovery, requestId]
Two ways to opt in:
// (a) fluent — no typed body
routes.MapPost<CreateOrderCommand, CreateOrderResponse>("/v1/orders", ...)
.ProducesProblem(422);
// (b) handler attribute — null responseType
public sealed class CreateOrderHandler : IRequestHandler<CreateOrderCommand, CreateOrderResponse>
{
[ApiResponse(422, null, "application/problem+json")]
public ValueTask<CreateOrderResponse> Handle(...) => ...;
}
Renderer options — F15 / F16 / F17
using Native.OpenApi;
using Native.OpenApi.Rendering;
var options = new OpenApiRendererOptions
{
Branding = new OpenApiBrandingOptions
{
PrimaryColor = "#0A2540",
AccentColor = "#00D4AA",
LogoUrl = "https://cdn.swepay.com.br/brand/logo-dark.svg",
FaviconUrl = "https://cdn.swepay.com.br/brand/favicon.ico",
FontFamily = "Inter, Roboto, sans-serif"
},
Footer = new OpenApiFooterOptions
{
StatusUrl = "https://status.swepay.com.br",
SupportUrl = "https://docs.swepay.com.br/support",
ChangelogUrl = "https://docs.swepay.com.br/changelog",
SlaUrl = "https://docs.swepay.com.br/sla",
TermsUrl = "https://docs.swepay.com.br/terms"
},
EnableMermaid = true, // F17 — render fenced ```mermaid blocks as SVG
MermaidFromLocalAsset = false // true for air-gapped deployments (serves ./assets/mermaid.min.js)
};
var renderer = new OpenApiHtmlRenderer();
var redocHtml = renderer.RenderRedoc ("/docs/openapi.json", "My API", options);
var scalarHtml = renderer.RenderScalar("/docs/openapi.json", "My API", options);
The legacy RenderRedoc(spec, title) / RenderScalar(spec, title) overloads are preserved (RFC principle O5).
Mermaid in descriptions — any fenced ```mermaid block inside summary/description text becomes inline SVG when EnableMermaid = true. Example description body:
## Flow
```mermaid
flowchart LR
A[1. Create realm] --> B[2. Register client] --> C[3. Issue token]
```
Minimal document pipeline
1. Loader
public class MyOpenApiDocumentLoader : OpenApiDocumentLoaderBase
{
public MyOpenApiDocumentLoader(OpenApiResourceReader reader) : base(reader) { }
public override IReadOnlyList<OpenApiDocumentPart> LoadCommon() => new[]
{
Load("common-schemas", "openapi/common/schemas.yaml"),
Load("common-responses", "openapi/common/responses.yaml"),
Load("common-security", "openapi/common/security.yaml"),
};
public override IReadOnlyList<OpenApiDocumentPart> LoadPartials() => new[]
{
Load("users", "openapi/users/openapi.yaml"),
Load("products", "openapi/products/openapi.json"),
};
}
2. Merger (optional)
public class MyOpenApiDocumentMerger : OpenApiDocumentMerger
{
protected override string GetServerUrl()
=> Environment.GetEnvironmentVariable("ENVIRONMENT") switch
{
"prd" => "https://api.swepay.com.br",
"hml" => "https://sandbox.api.swepay.com.br",
_ => "https://localhost:5001"
};
protected override string GetApiTitle() => "Swepay API";
protected override string GetApiDescription() => "Consolidated OpenAPI spec for partner integrations.";
}
3. Provider
var reader = new OpenApiResourceReader(typeof(Program).Assembly, "MyApp.");
var loader = new MyOpenApiDocumentLoader(reader);
var merger = new MyOpenApiDocumentMerger();
var linter = new OpenApiLinter(OpenApiLintOptions.Empty);
var provider = new OpenApiDocumentProvider(loader, merger, linter);
provider.WarmUp();
var json = provider.Document.Json;
var yaml = provider.Document.Yaml;
Linting
var options = new OpenApiLintOptions(
RequiredErrorResponses: ["400", "401", "500"],
SensitiveFieldNames: ["password", "token", "secret"],
DisallowedGenericSegments: ["data", "items"]);
var linter = new OpenApiLinter(options);
Checks:
- OpenAPI version is
3.1.0 - Every path is versioned (e.g.
/v1/) - Every operation has a security block (or explicit
security: []for anonymous) - Required error responses are declared
- Sensitive fields carry a description
[ApiResponse] on handler methods (v1.6.0+)
Co-locate per-status responses with the handler:
using Native.OpenApi;
using NativeMediator;
public class GetProductHandler : IRequestHandler<GetProductCommand, GetProductResponse>
{
[ApiResponse(200, typeof(GetProductResponse))]
[ApiResponse(404, typeof(ErrorResponse))]
[ApiResponse(400, typeof(ProblemDetails), "application/problem+json")]
[ApiResponse(422, null, "application/problem+json")] // → SwepayProblemDetails (F13)
public ValueTask<GetProductResponse> Handle(GetProductCommand r, CancellationToken ct) { ... }
}
| Parameter | Type | Default | Purpose |
|---|---|---|---|
statusCode |
int |
— | HTTP status |
responseType |
Type? |
null |
typed body; when null + application/problem+json, the generator points at SwepayProblemDetails |
contentType |
string |
"application/json" |
content type |
Native AOT
<PropertyGroup>
<PublishAot>true</PublishAot>
<IsAotCompatible>true</IsAotCompatible>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
All JSON/YAML serialization flows through source-generated JsonSerializerContext. No runtime reflection on consumer types.
Related
- NativeLambdaRouter.SourceGenerator.OpenApi — Roslyn generator that reads the attributes above
- Root repository · Changelog · UX RFC
License
MIT
| 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
- YamlDotNet (>= 16.3.0)
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 |
|---|---|---|
| 1.7.0 | 116 | 4/17/2026 |
| 1.6.0 | 1,234 | 2/23/2026 |
| 1.5.1 | 469 | 2/19/2026 |
| 1.5.0 | 109 | 2/18/2026 |
| 1.4.1 | 101 | 2/18/2026 |
| 1.4.0 | 66 | 2/17/2026 |
| 1.3.3 | 137 | 2/13/2026 |
| 1.3.1 | 124 | 2/11/2026 |
| 1.3.0 | 71 | 2/11/2026 |
| 1.2.6 | 72 | 2/11/2026 |
| 1.2.5 | 78 | 2/11/2026 |
| 1.2.4 | 73 | 2/11/2026 |
| 1.2.3 | 74 | 2/9/2026 |
| 1.2.2 | 70 | 2/3/2026 |
| 1.2.1 | 79 | 2/3/2026 |
| 1.2.0 | 77 | 2/3/2026 |
| 1.1.0 | 81 | 2/2/2026 |
| 1.0.0 | 163 | 2/1/2026 |