apigenie-generatorOpenAPI clientsclient csharp

client csharp

Generate a C# client of an OpenAPI document, built on HttpClient and System.Text.Json, for .NET 8 (or 9 and 10).

From an AsyncAPI document, the command generates a C# Kafka client library instead; see apigenie-generator client csharp (AsyncAPI).

Usage

apigenie-generator client csharp [...OPTIONS] INPUT_FILE

Description

The input must be an OpenAPI 3.0, 3.1 or 3.2 document. The output is a class library: a .csproj targeting net8.0 with nullable reference types, which references only Microsoft.Extensions.Http (for the registration with dependency injection).

services.AddPetStoreClient(options =>
{
    options.BaseUrl = new Uri("https://api.example.com/v1");
    options.ApiKey = ApiCredential.Static(configuration["PetStore:ApiKey"]!);
});

// inject PetStoreClient, or the client of a group, e.g. IPetsClient
var pets = await client.Pets.ListPetsAsync(limit: 10, cancellationToken: token);
try
{
    var pet = await client.Pets.GetPetAsync(42);
}
catch (ApiException error) when (error.StatusCode == 404)
{
    Console.WriteLine(error.Problem?.Detail);
}

Without dependency injection, PetStoreClient.CreateHttpClient(options) makes an HttpClient that sends the credentials: new PetStoreClient(PetStoreClient.CreateHttpClient(options), options).

The generated code has:

  • Clients. One interface and one client per group of operations (IPetsClient, PetsClient), grouped by their first tag (see grouping), and PetStoreClient with a property per group. The clients take the HttpClient in their constructor, so IHttpClientFactory manages them; AddPetStoreClient() registers them as typed clients, with the options and the handlers sending the credentials. Operations marked x-internal are left out.
  • Methods. An async method per operation, named after its operation ID (ListPetsAsync, see action_naming): path parameters, the other required parameters and the body first, optional parameters (null leaves them out) and a CancellationToken last. Parameters are sent in their style (form, spaceDelimited, pipeDelimited, deepObject, simple, label, matrix), objects included; parameters described by content are sent as JSON. Deprecated operations are [Obsolete].
  • Bodies. JSON (with the media type of the document, e.g. application/merge-patch+json), text, binary (Stream), JSON Lines uploads (IAsyncEnumerable<T>), and forms and multipart bodies as *Form classes, whose files are FileParts (format: binary or contentMediaType) sent with the content type of their encoding.
  • Responses. The method returns the body of the success response (Task<Pet>), nothing (Task), or, when statuses have different bodies, a *Result with the status, the headers and a property per status. Responses with a status of 400 or more throw an ApiException with the status, the headers, the body as text and its RFC 9457 problem details (Problem). text/event-stream responses are IAsyncEnumerable<ServerSentEvent<T>>, typed by the contentSchema of the events’ data (OpenAPI 3.2 itemSchema, or x-itemSchema), JSON Lines responses IAsyncEnumerable<T>, both read as they arrive; binary responses are BinaryResponses to dispose. Each other media type of a body or a response is a method of its own, named after it: UpdatePetJsonPatchAsync, GetLabelPdfAsync, GetEventsEventStreamAsync.
  • Servers. Requests go to BaseUrl, else to the base address of the HttpClient, else to the first server of the operation, of its path or of the API, with ServerVariables for their variables.
  • Security. A credential per security scheme in the options (ApiCredential.Static, ApiCredential.FromProvider for tokens that change, ApiCredential.Basic). A request carries the credentials of the first alternative of its operation’s security requirements whose credentials are set; the AuthorizationHandler (HTTP schemes such as bearer and basic, OAuth 2.0, OpenID Connect) and ApiKeyHandler (headers, query parameters, cookies) delegating handlers add them.
  • Models. Records and enums, as apigenie-generator server aspnet writes them: required/init properties, DataAnnotations for constraints (exclusive bounds of OpenAPI 3.0 and 3.1 alike), nullable types for nullable, null types and oneOf with null. allOf is merged into one record, or is inheritance ([JsonPolymorphic]/[JsonDerivedType]) when the base has a discriminator. oneOf and anyOf with a discriminator are abstract records whose variants are told apart by the discriminator value (explicit mapping, or the const of the discriminator property, or the schema name), read and written by generated converters wherever the discriminator is in the object; values with an unknown discriminator are read as the default mapping (defaultMapping, or x-defaultMapping). Unions whose members have one C# type (e.g. strings of different formats) have that type.
  • JSON. A System.Text.Json source-generated JsonSerializerContext (PetStoreJsonContext) with every model and body type, so the clients read and write JSON without reflection. Use it to read error bodies: JsonSerializer.Deserialize(error.Content!, PetStoreJsonContext.Default.Error).

The project also has README.md, Infrastructure/ (the runtime of the clients) and Serialization/ (the JSON context and converters). The code builds with -warnaserror and the .NET analyzers.

Not supported: callbacks and webhooks, OAuth 2.0 flows (the caller supplies access tokens), mutual TLS (configure the client certificate on the HTTP handler), XML bodies (sent and read as text), prefixItems, patternProperties, if/then/else, dependentRequired, dependentSchemas, unevaluatedProperties, multipleOf, retries (add a resilience handler with configureHttpClient) and pagination.

Extensions

Extension Effect
x-apigenie.codegen.controller-name on an operation or its path item: the group (client) of the operation, whatever grouping says
x-apigenie.codegen.action-name name of the method of an operation, when action_naming lists extension
x-apigenie.codegen.disabled true on an operation or its path item leaves the operation out, unless skip_disabled is false
x-internal true on an operation leaves it out
x-enum-varnames, x-enum-names names of the members of an enum, in PascalCase
x-enum-descriptions, x-enumDescriptions documentation comments of the members of an enum
x-defaultMapping defaultMapping of a discriminator in OpenAPI 3.0 and 3.1 documents: the schema of the values whose discriminator value isn’t mapped
x-itemSchema itemSchema of a media type in OpenAPI 3.0 and 3.1 documents: the type of the items of a streamed response

The extensions can live in an overlay for this target instead of in the document, e.g. csharp.overlay.yaml:

overlay: 1.0.0
info:
  title: C# names
  version: 1.0.0
actions:
  - target: $.components.schemas.PetStatus
    update:
      x-enum-varnames: [Available, OnHold, Sold]
  - target: $.paths['/pets']
    update:
      x-apigenie:
        codegen:
          controller-name: Animals
apigenie-generator client csharp openapi.yaml --overlay csharp.overlay.yaml -o out

Options

-c, –config

Path to the configuration file to use. See the “Configuration” section for more information.

–overlay OVERLAY

Path to an OpenAPI Overlay (1.0 to 1.2) applied to the input before generating, e.g. one setting the extensions of the “Extensions” section. Can be repeated: the overlays apply in order, after those listed under commands.generate.client_csharp.overlay in the configuration file (with paths relative to it). Warnings and errors about a value an overlay set point to the overlay.

–strict

Makes an overlay action whose target selects nothing an error (exit status 2, nothing is generated) instead of a warning (OVERLAY:LINE:COL: action #N (TARGET) selects no nodes.).

-o, –output

Directory where the project is generated.

–package-name

Root namespace of the generated code, e.g. Acme.PetStore.Client; also the name of the project. The API-wide types are named after its last segment (or the one before when it is Client, Clients or Sdk): PetStoreClient, PetStoreClientOptions. Defaults to the title of the API followed by .Client.

–package-version

Version of the project. Defaults to the version of the API when it’s a valid .NET version, or 1.0.0.

-p, –property NAME=VALUE

Sets an option (see below), e.g. -p layout=sources; can be repeated. Options given on the command line take precedence over the configuration file. OpenAPI documents only: using it with an AsyncAPI document is an error.

Configuration

The options are read from commands.generate.client_csharp. Its asyncapi section configures the generation from AsyncAPI documents (see apigenie-generator client csharp (AsyncAPI)).

version: '1.0'
commands:
  generate:
    client_csharp:
      namespace: Acme.PetStore.Client
      target_framework: net9.0
      grouping: tag
      dependency_injection: true
Option Values Description
layout project, sources project (default): a class library (.csproj, README.md and the sources); sources: only the generated sources, for an existing project
namespace namespace Root namespace of the generated code (also --package-name); derived from the API title by default, e.g. PetStore.Client
target_framework net8.0, net9.0, net10.0 Target framework of the project (default: net8.0)
grouping tag, url Grouping of operations into clients: by their first tag (default), or by the first segment of their paths
action_naming id, extension, url Sources of method names, in order of preference (default: id, url)
dependency_injection true, false Generate the registration of the clients with IServiceCollection and IHttpClientFactory (default: true; it references Microsoft.Extensions.Http)
skip_deprecated true, false Don’t generate deprecated operations (default: false)
skip_disabled true, false Don’t generate operations disabled with the x-apigenie extension (default: true)
model_suffix text Suffix of model names, e.g. Dto
version version Version of the project (also --package-version); defaults to the version of the API

Unknown options are errors.