Skip to content

Features

Incremental source generation

OpenApiWeaver leverages the Roslyn incremental generator pipeline for fast, cached rebuilds. Only the documents that have changed are re-processed, making it efficient even in large solutions with multiple OpenAPI files.

JSON & YAML support

Reads OpenAPI 3.0-3.2 documents in the following formats:

  • .json - OpenAPI 3.x JSON
  • .yaml / .yml - OpenAPI 3.x YAML

Tag-based sub-clients

Operations are grouped by their OpenAPI tags and exposed as properties on the root client. Each tag becomes a separate sub-client class:

csharp
var client = new PetstoreClient(accessToken: "token");

// "Pets" tag -> client.Pets property
var pet = await client.Pets.GetAsync(petId: 1);

// "Users" tag -> client.Users property
var user = await client.Users.GetAsync(userId: "me");

Method names are derived from operationId when available, with an Async suffix appended automatically.

When a tag has a description, it is emitted as XML documentation on the generated tag property and sub-client class.

Typed request / response models

Generates sealed classes for object schemas and maps them to strongly typed method parameters and return values:

  • Object schemas -> sealed class with [JsonPropertyName] attributes
  • Enums -> readonly record struct with a dedicated JsonConverter (string enums), or standard enum (integer enums) — see Schema Type Mapping
  • Arrays -> IReadOnlyList<T>
  • Dictionaries (additionalProperties / patternProperties) -> IReadOnlyDictionary<string, T>
  • Inline object / enum schemas -> nested types under the owning model class

Naming conventions are automatically converted from snake_case to PascalCase for C# idiomatic use, while preserving the original JSON names via [JsonPropertyName].

Optional (non-required) nullable properties are annotated with [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] so they are omitted from the JSON payload when null.

OpenAPI 3.2 nullable type arrays such as type: ["string", "null"] are mapped to nullable CLR types like string?.

Inline / nested schemas

Inline object or enum schemas that appear inside property definitions, array items, or additionalProperties are emitted as nested types under the owning generated model class. This keeps the type hierarchy compact and avoids excessively long top-level type names.

csharp
public sealed class Order
{
    [JsonPropertyName("status")]
    public Order.StatusEnum? Status { get; init; }

    // Inline enum schema generated as a nested type
    public readonly record struct StatusEnum(string Value)
    {
        public static readonly StatusEnum Placed = new("placed");
        public static readonly StatusEnum Approved = new("approved");
    }
}

Response types

The generated methods return different types based on the response content:

Response ContentReturn Type
application/jsonTask<T> with the deserialized response type
text/plain or similarTask<string>
Binary contentTask<byte[]>
No content (e.g. 204)Task

When multiple successful responses are available, OpenApiWeaver prefers a response with a body over a lower-status no-content response. For multiple response media types, JSON is preferred first, then binary, then text.

Runtime error handling

Generated clients do not call EnsureSuccessStatusCode(). Instead, they emit explicit error handling code for non-success responses so OpenAPI error definitions can be preserved.

  • Non-2xx responses throw OpenApiException
  • The exception includes StatusCode, ReasonPhrase, ContentType, and the raw ResponseContent
  • If the OpenAPI operation defines a matching error response schema, the generated code throws OpenApiException<TError> with the deserialized error payload in Error
  • Matching prefers exact status codes first, then wildcard patterns such as 4XX, then default

For JSON error responses, deserialization is attempted only when the response content type is JSON-compatible. If deserialization fails, the client falls back to the non-generic OpenApiException and preserves the raw response body for troubleshooting.

For successful JSON responses that are expected to have a body, an empty response body results in InvalidOperationException("The response body was empty.").

Example:

csharp
try
{
    await client.Partners.CreatePartnerAsync(body, cancellationToken);
}
catch (OpenApiException<ValidationProblem> exception) when (exception.StatusCode == 400)
{
    Console.WriteLine($"Validation failed: {exception.Error?.Message}");
}
catch (OpenApiException exception)
{
    Console.WriteLine($"Request failed: {exception.StatusCode} {exception.ReasonPhrase}");
    Console.WriteLine($"Content-Type: {exception.ContentType}");
    Console.WriteLine(exception.ResponseContent);
}

Multiple request body formats

Supports the following content types for request bodies:

Content TypeSerialization
application/jsonJsonSerializer with JsonSerializerDefaults.Web
application/x-www-form-urlencodedFormUrlEncodedContent
multipart/form-dataMultipartFormDataContent (supports binary file uploads)

If a request body declares multiple supported media types, JSON is preferred when available.

Compile-time-only policy for form / multipart

For application/x-www-form-urlencoded and multipart/form-data request bodies, all code must be emittable entirely at compile time. If this is not possible, generation fails with OAW004.

Supported form and multipart request bodies require:

  • A schema reference to components/schemas
  • An object shape whose properties are known at generation time
  • Property types that map directly to CLR types (scalars, byte[], supported collections)

The following are intentionally unsupported for form / multipart request bodies:

  • Inline request body schemas
  • oneOf / anyOf
  • additionalProperties / patternProperties

Parameter locations

Parameters defined in the OpenAPI document are mapped to method parameters based on their location:

LocationBehavior
pathInterpolated into the URL path
queryAppended as query string parameters
headerAdded as request headers
cookieAdded to the Cookie header

Security scheme support

Constructor parameters are automatically generated based on the security schemes defined in the OpenAPI document:

SchemeGenerated Parameter
OAuth2 / Bearer tokenstring accessToken - sent as Authorization: Bearer {token}
API key (header)string apiKey - sent as a custom request header
API key (query)string apiKey - appended to the query string
API key (cookie)string apiKey - sent in the Cookie header

Multiple security schemes can be combined. For example, if an API requires both an OAuth2 token and an API key, the constructor will accept both parameters.

All HTTP methods

Supports all standard HTTP methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, and TRACE.

Build-time diagnostics

Reports errors and warnings as standard compiler diagnostics during compilation:

CodeSeverityDescription
OAW001ErrorOpenAPI document is empty
OAW002WarningOpenAPI document has validation warnings
OAW003ErrorOpenAPI document is invalid (e.g. malformed JSON/YAML)
OAW004ErrorOpenAPI document uses an unsupported feature

These diagnostics appear in the Visual Studio Error List, dotnet build output, and CI logs, just like any other compiler diagnostic.

Generated client extensibility

The root client class is generated as a partial class, so you can extend it with additional methods or properties in a separate file without modifying the generated code.

The root client also implements IDisposable and disposes the internal HttpClient when Dispose() is called.

XML documentation comments

The generated code includes <summary>, <remarks>, <param>, and <returns> XML doc comments derived from info, tag descriptions, operation summary / description, parameter descriptions, response summary / description, and schema title / description. HTML markup is stripped automatically so the generated IntelliSense stays clean.

Released under the MIT License.