Generating clients

OpenAPI.client reads an OpenAPI 3.0, 3.1, or 3.2 document and emits one deterministic Julia module. Load HTTP before reading a URL; local files and inline JSON or YAML do not need HTTP during generation.

using OpenAPI, HTTP

OpenAPI.client(
    "https://example.com/openapi.yaml";
    name = "ExampleClient",
    path = "ExampleClient.jl",
)

The long form runs the same pipeline in stages, which lets an application inspect or cache the intermediate values (see Pipeline and diagnostics):

source = OpenAPI.load("https://example.com/openapi.yaml")
api = OpenAPI.normalize(source)
plan = OpenAPI.plan(api; name = "ExampleClient")
OpenAPI.client(plan; path = "ExampleClient.jl")

The generated file imports OpenAPI, HTTP, and JSON. It also imports the Julia standard libraries Base64, Dates, and UUIDs. Add the three package dependencies to the environment that will include the generated file.

Calling operations

include("ExampleClient.jl")

client = ExampleClient.Client(
    "https://api.example.com";
    headers = ["User-Agent" => "my-app/1.0"],
)

# Each operationId becomes a Julia function. Path parameters are positional.
# Other parameters are keywords. A required request body is the last positional
# argument. Pass `client=client` to avoid shared global configuration.
result = ExampleClient.get_widget("widget-123"; verbose = true, client)

Optional model fields use ExampleClient.Absent, not nothing. This keeps a missing value distinct from an explicit JSON null.

model = ExampleClient.WidgetInput(
    name = "example",
    description = ExampleClient.ABSENT,
)

Responses and errors

Pass with_http_info=true to receive an ApiResponse with the status, raw headers, decoded documented headers, and typed body. A non-2xx response throws ApiError. The error keeps the raw body even when documented error decoding fails.

Responses are decoded by status alone when a server omits its Content-Type header, or misreports it while only one media type is documented for that status; UnexpectedContentType is thrown only when several documented media types make the choice ambiguous. A 2XX status the document does not describe never fails the call: an empty body returns nothing and a payload returns raw bytes. Undocumented error statuses still throw ApiError.

Request options and content negotiation

Use content_type=... and accept=... on an operation when the document offers more than one representation. Use request_headers for one call and Client(headers=...) for all calls. request_options passes options to the HTTP transport. Streaming calls default to HTTP/1.1 because consumer-driven stream cancellation closes one request connection. Set protocol=:auto or :h2 in request_options when the caller accepts HTTP/2 stream lifecycle semantics. Buffered calls keep HTTP.jl's automatic protocol selection.

Extra percent-encoding in path parameters

Generated clients percent-encode path parameters per RFC 3986, which leaves the unreserved characters A-Z a-z 0-9 - _ . ~ as they are. Some servers cannot route a path segment that contains a literal .: Rails, for example, ends a dynamic segment at the first . and reads the rest as a format suffix, so GET /customers/acme.example.com-42 is a 404 while GET /customers/acme%2Eexample%2Ecom-42 matches. escape_path_chars names characters to percent-encode in addition to the standard set:

client = ExampleClient.Client("https://api.example.com"; escape_path_chars = ".")
ExampleClient.get_customer("acme.example.com-42"; client)
# GET /customers/acme%2Eexample%2Ecom-42

The option applies to the values of every path parameter of every operation and defaults to empty. Style delimiters (. for label, ; and = for matrix) and the parameter name from the path template are never touched. RFC 3986 treats the encoded and unencoded spellings of an unreserved character as the same identifier, so servers that decode before routing are unaffected. This is independent of allowReserved, which removes escaping rather than adding it, and it is not a substitute for it: a / in a value is still encoded unless the parameter declares allowReserved: true.

HTTP behavior

Generated clients support:

  • path, query, header, and cookie parameters;
  • simple, label, matrix, form, spaceDelimited, pipeDelimited, and deepObject serialization where the specification permits each style, plus the bracket-path deepObject extension for arrays and nested values (see deepObject bracket paths);
  • allowReserved, allowEmptyValue, explode defaults, and parameter content. allowReserved: true is honoured on path parameters too, so a slash-delimited value such as an OPA document path is sent as-is instead of with every / percent-encoded. OAS 3.2 documents this for path parameters and 3.0 tolerates it, but 3.1 allows allowReserved only on query parameters, so a 3.1 document that declares it on a path parameter fails validation when the document is loaded;
  • JSON and structured-suffix JSON media types;
  • text and binary bodies;
  • application/x-www-form-urlencoded bodies;
  • multipart bodies, per-property encodings, documented part headers, uploads, and one required level of nested named OAS 3.2 encoding;
  • JSON Lines, NDJSON, JSON text sequences, and GeoJSON text sequences when the body is described by a normal schema;
  • exact, wildcard, and structured-suffix media negotiation;
  • exact response codes, 1XX through 5XX ranges, and default responses;
  • documented response headers, including repeated headers and Set-Cookie;
  • operation, path, and root servers, relative server URLs, named servers, and validated server variables;
  • request and response validation with input/output JSON Schema semantics.

Date and time mapping

format: date-time maps to Dates.DateTime by default, decoding RFC 3339 offsets by normalizing to UTC. Generate with datetime = :zoned to map to TimeZones.ZonedDateTime instead, preserving offsets end to end; the generated module then depends on TimeZones.jl.

Source privacy

Generated schema graphs use content-derived resource identifiers. Local paths, source URL userinfo, and source URL query strings are not embedded in generated files. A relative Server Object still depends on the public scheme, host, and path of the source URL because that location is part of the OpenAPI resolution rule.