apigenie-generatorOpenAPI clientsclient go

client go

Generate a Go client for the net/http package of Go 1.22 from an OpenAPI document.

From an AsyncAPI document, the command generates a Go Kafka client package instead; see apigenie-generator client go (AsyncAPI).

Usage

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

Description

The generator writes a Go package whose code uses the standard library only: its go.mod has no requirements. It shares the types, validation and parameter codecs with apigenie-generator server go, so a client and a server generated from the same document read and write values the same way.

File Contents
types.gen.go the types of the component schemas the operations use, and of their inline schemas, with Validate() methods
client.gen.go DefaultBaseURL, Servers, SecuritySchemes, Operations, NewClient, an option per security scheme, and per operation its parameters, bodies, response type and methods
runtime_*.gen.go the Client, requests and errors, parameter encoding, streams, multipart bodies, validation, Nullable[T], Date
go.mod with layout: module
client, err := api.NewClient(
    api.WithBaseURL("https://api.example.com/v1"), // DefaultBaseURL otherwise
    api.WithAPIKey(os.Getenv("API_KEY")),
    api.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}),
)
if err != nil {
    return err
}
res, err := client.GetPet(ctx, 42, nil)
var apiErr *api.APIError
switch {
case errors.As(err, &apiErr):
    log.Printf("status %d: %v", apiErr.StatusCode, apiErr.Problem) // RFC 9457 problem details
case err != nil:
    return err // transport, encoding or decoding error
default:
    fmt.Println(res.JSON200.Name)
}

Methods

Every operation is a method of Client, named after its operationId, whose first argument is a context.Context, followed by the path parameters, a pointer to a struct with the query, header and cookie parameters (nil when none is set), the body, and request editors changing the request before it is sent:

func (c *Client) CreatePet(ctx context.Context, params *CreatePetParams, body NewPet, reqEditors ...RequestEditorFn) (*CreatePetResponse, error)
func (c *Client) CreatePetWithBody(ctx context.Context, params *CreatePetParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreatePetResponse, error)
  • An operation with several request media types gets a method per media type (UpdatePetWithApplicationMergePatchJSONBody; the bare name sends application/json), and always a ...WithBody method taking the media type and an io.Reader.
  • JSON bodies are values of their types, validated with Validate() before they are sent (validate_requests: false turns that off); text/* bodies are strings, other media types io.Reader values, and application/jsonl bodies slices of the items of their itemSchema.
  • application/x-www-form-urlencoded bodies are encoded field by field with the styles of their encoding (deepObject, delimited arrays, JSON fields).
  • Parameters are serialized in their style and explode mode: simple, label and matrix path parameters, form, spaceDelimited, pipeDelimited and deepObject query parameters, allowReserved, headers and cookies. Parameters described with content are sent as JSON.
  • Methods set the Accept header to the media types of the documented responses.
  • Operations marked x-internal: true (on the operation or its path) are left out, unless internal_operations is set.

Responses

A method returns a response struct with one field per documented status and media type, set for the response received, next to the raw *http.Response:

type GetPetResponse struct {
    HTTPResponse *http.Response
    Body []byte                                // the body, when it was read
    Headers200 *GetPet200Headers                // typed response headers
    JSON200 *Pet                                // 200, application/json
    ApplicationPdf200 io.ReadCloser             // 200, application/pdf: left open
    ApplicationProblemJSON404 *ProblemDetails   // 404, application/problem+json
    ApplicationProblemJSONDefault *ProblemDetails
}
  • The field names join the media type (JSON for application/json, Text for text/plain, EventStream, JSONL, the media type otherwise) and the status (200, 2XX, Default).
  • application/problem+json contents are *ProblemDetails (type, title, status, detail, instance and the other members in Extensions), whatever their schema.
  • Binary contents and streams are left open: close the field, or the response with Close(). Other bodies are read, kept in Body, and closed.
  • For a status of 400 or more, the method returns the response and an *APIError with the status, the problem details of an application/problem+json body, the body and the response. errors.As(err, &problem) also finds the *ProblemDetails.
  • An undocumented status or media type sets no field: Body holds the body.
  • Response headers that can’t be parsed are left unset; their text stays in HTTPResponse.Header.

Streams

text/event-stream and application/jsonl contents are iterators over their items, typed by the itemSchema of the media type (OpenAPI 3.2; x-itemSchema in 3.0 and 3.1 documents):

res, err := client.GetTrackingEvents(ctx, id, nil)
if err != nil {
    return err
}
defer res.Close()
if events := res.EventStream200; events != nil {
    for events.Next() {
        event := events.Item() // the itemSchema type, filled from id, event, data and retry
        fmt.Println(event.Event, event.Data)
    }
    if err := events.Err(); err != nil {
        return err
    }
}
  • JSONLStream[T] reads one JSON value per line. EventStream[T] follows the server-sent events parsing rules of the HTML standard (data lines joined, comments, id, retry); Event() returns the event as received and LastEventID() the ID to resume from with Last-Event-ID.
  • Without itemSchema, server-sent events are ServerSentEvent values and JSON Lines items json.RawMessage values.
  • Go 1.22 has no range-over-func iterators, hence Next(), Item() and Err().

Multipart bodies

multipart/form-data bodies of object schemas are structs with a field per part:

  • Binary parts (format: binary, or contentMediaType in 3.1 and 3.2) are File values (a name, a media type, a reader and extra part headers), arrays of them []File. A file part is sent with the contentType of its encoding, or its contentMediaType, unless File.ContentType is set. Headers the encoding declares are set through File.Header.
  • Object parts, and parts whose encoding has a JSON contentType, are sent as JSON; other parts as text, a part per item of arrays.

The body is built in memory and sent with its boundary.

Servers

DefaultBaseURL is the first server of the document with an absolute URL, the default values of its variables applied; WithBaseURL sets another one, and is required when there is none. Operations with servers of their own (on the operation or its path) are sent there, a relative server URL being resolved against the base URL; WithOperationServers(false) sends every request to the base URL instead, e.g. to a test server or a proxy.

Security

Every security scheme of the document gets an option setting its credentials, named With followed by the name of the scheme:

Scheme (name in the document) Option
apiKey in a header, the query or a cookie (apiKey) WithAPIKey(key string)
http bearer, oauth2, openIdConnect (oauth2) WithOauth2(token string), sent as a bearer token
http basic (basicAuth) WithBasicAuth(username, password string)

A request gets the credentials of the first alternative of its operation’s security requirements whose schemes all have credentials, so an API key in the query is only sent to the operations that accept it; an operation with security: [] gets none. WithSecurity(scheme, editor) sets the credentials of any scheme with a RequestEditorFn; obtaining and refreshing OAuth2 tokens is up to the caller. mutualTLS needs a client certificate in the transport of the http.Client (WithHTTPClient).

Types

  • Optional properties are pointers with omitempty; slices, maps and any stay values. optional_fields: omitzero uses values with omitzero (Go 1.24).
  • Optional properties that can be null are Nullable[T], which tells absent, null and set values apart; required ones are pointers. nullable_type: pointer uses pointers for both.
  • Enums are defined types with constants named after the type and the value (PetStatusAvailable).
  • oneOf and anyOf schemas are structs with one pointer per variant. With a discriminator, the value goes to the variant its property selects: through the mapping, the name of the schema or the value the schema gives the property (const); other values go to the defaultMapping variant (x-defaultMapping in 3.0 and 3.1 documents), or are kept in Raw. Without a discriminator, the value goes to the first variant it decodes into.
  • allOf schemas are merged into one struct; an anyOf or oneOf whose members only list required properties (anyOf: [{required: [email]}, {required: [phone]}]) is checked by Validate().
  • date-time strings are time.Time, date strings Date, byte and binary strings []byte.

Extensions

Extension Effect
x-go-name name of the type of a schema, or of the field of a property
x-go-type Go type of a schema, e.g. decimal.Decimal; with x-go-type-import: {path: ...} for its package
x-go-type-skip-optional-pointer the optional property isn’t a pointer
x-go-json-ignore the field gets the tag json:"-"
x-omitempty true or false overrides the omitempty of the field
x-enum-varnames, x-enumNames names of the enum constants
x-enum-descriptions comments of the enum constants
x-apigenie.codegen.name name of the type of a component schema, when it has no x-go-name
x-apigenie.codegen.action-name name of the method of an operation without an operationId (with the default action_naming, which lists id first)
x-apigenie.codegen.disabled true on an operation or its path item leaves the operation out
x-internal true on an operation or its path item leaves the operation out, unless internal_operations is set
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. go.overlay.yaml:

overlay: 1.0.0
info:
  title: Go types
  version: 1.0.0
actions:
  - target: $.components.schemas.Pet
    update:
      x-go-name: Animal
  - target: $.components.schemas.Pet.properties.price
    update:
      x-go-type: decimal.Decimal
      x-go-type-import:
        path: github.com/shopspring/decimal
apigenie-generator client go openapi.yaml --overlay go.overlay.yaml -o out

Limitations

  • Callbacks, webhooks and links aren’t generated: they describe requests the server makes or documentation.
  • Validate() doesn’t check prefixItems, patternProperties, if/then/else, dependentRequired, dependentSchemas and unevaluatedProperties; patterns using lookaround or backreferences aren’t checked either.
  • Multipart bodies are built in memory; there are no retries or rate limiting beyond what the http.Client does.
  • Exported names of the runtime (Client, File, Server, Operation, ProblemDetails, Date, Nullable, …) are reserved: schemas with these names get numbered types, e.g. File2.
  • The input must be an OpenAPI 3.0, 3.1 or 3.2 document; convert Swagger 2.0 documents with apigenie convert, and bundle documents referencing other files with apigenie bundle.

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_go.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 package is generated.

–package-name

Name of the Go package. Defaults to the name of the output directory (api when it isn’t a valid package name).

–package-version

Ignored: Go packages have no version.

-p, –property NAME=VALUE

Sets a generator option (see below), e.g. -p layout=module. Options given on the command line take precedence over the configuration file. Lists are comma-separated (-p include_tags=pets,store) and mappings too (-p type_mappings=uuid=github.com/google/uuid.UUID).

Configuration

version: '1.0'
commands:
  generate:
    client_go:
      package: petstore
      layout: module
      module: github.com/acme/petstore
      type_mappings:
        uuid: github.com/google/uuid.UUID
Option Type Default Description
package string name of the output directory Name of the Go package; also set by --package-name.
layout package or module package module also writes go.mod, making the output a module of its own.
module string example.com/<package> Module path written to go.mod.
optional_fields pointer or omitzero pointer Optional properties as pointers with omitempty, or as values with omitzero (Go 1.24).
nullable_type tristate or pointer tristate Optional nullable properties as Nullable[T], or as pointers.
include_tags list of strings Generates only the operations with one of these tags.
exclude_tags list of strings Skips the operations with one of these tags.
skip_prune boolean false Keeps the component schemas that no generated operation uses.
internal_operations boolean false Also generates the operations marked x-internal: true.
validate_requests boolean true Validates request bodies against their schemas before sending them.
initialisms list of strings Words written in upper case in identifiers, besides ID, URL, HTTP, JSON and the other initialisms of golint.
type_mappings map of strings Go types of string formats or x-go-type names, e.g. uuid: github.com/google/uuid.UUID.
action_naming list of id, extension, url id, extension, url Where method names come from, in order of preference.