This project exists for its configuration capabilities. Most API client libraries are either feature complete (due to code generation) or ergonomic (due to hand-crafting code), but not both. With the right configuration — and a little bit of luck with the OpenAPI description — your client library can be both.
The following sections outline the configuration available for the default implementations of the various callbacks used by this library. See the Plugins documentation for information about overriding the default behaviours with custom implementations.
This library uses configuration profiles to allow for multiple sets of configuration at once. In practice, this means that all configuration lives under a top-level key that will be referenced when running the generator. For example:
# config/config.exs
import Config
config :oapi_generator,
my_profile: [
output: [
base_module: MyClientLibrary
]
]
To use this configuration, run mix api.gen my_profile path/to/description.json
or call OpenAPI.run("my_profile", ["path/to/description/json"])
.
Not sure what to name the profile? Start with
default
.
Operations are the actions you can take in an API: the HTTP requests that are possible. This code generator takes an operation-first mindset when generating code. For example, schemas that are defined in a description but not referenced in any API operation will not be output.
Following is an example operation module that has been annotated with the configuration options involved in various parts of the rendered output.
# Existence: `ignore`
# Location: `output.location`, `output.operation_subdirectory`
# Module name: `output.base_module`, `naming.default_operation_module`
defmodule Example.Operations do
@moduledoc """
Provides API endpoints related to repos
"""
# `output.operation_use`, `output_schema_use` (if schemas are present)
use Example.Helper
# `output.default_client`
@default_client Example.Client
# Schemas would appear here, if present.
@doc """
Summary of the operation
"""
# `output.base_module`, `output.types.error`
@spec my_operation(String.t(), map, keyword) :: :ok | {:error, Example.Error.t()}
def my_operation(path_param, body, opts \\ []) do
client = opts[:client] || @default_client
query = Keyword.take(opts, [:query_param])
client.request(%{
args: [path_param: path_param, body: body],
call: {Example.Operations, :my_operation},
url: "/path/to/#{path_param}",
body: body,
method: :post,
query: query,
request: [{"application/json", :map}],
response: [{200, :map}, {404, {Example.NotFoundError, :t}}],
opts: opts
})
end
end
Below are some of the highlights from the available configuration. See the Option Reference section for an exhaustive list of the available options.
At the moment, configuration related to the naming and placement of operation functions is limited. If you have ideas related to this, please share them in GitHub Discussions.
If an operation does not have any tags, and its operation ID does not contain any /
characters, it will be placed in a default module named [BaseModule].Operations
.
This module name can be controlled using the naming.default_operation_module
configuration:
config :oapi_generator, default: [
naming: [
base_module: MyClientLibrary,
default_operation_module: MyOperations
]
]
Learn more in the documentation for OpenAPI.Processor.Naming.operation_modules/2
.
The ignore
option allows operations to be ignored based on their Operation IDs or paths in the API description.
Patterns can be exact strings or regular expressions.
Learn more in the documentation for OpenAPI.Processor.Ignore
.
output.default_client
defines the module that will be called in each operation function.
By allowing the client module to be defined at the time of code generation (with this option) and at runtime (with the client
option passed to any operation call), this code generator maximizes flexibility.
For example, tests can supply a special test client module when they call client operations.
This need not be the end of the flexibility, however. The GitHub client library defines a "stack" system within its client module, allowing users to easily swap libraries for handling HTTP requests and decoding JSON.
output.operation_use
defines a module that should be included as a use [output.operation_use]
statement at the top of every module that contains operations.
This can be useful for meta-programming, such as using compilation hooks to performed advanced modifications of the generated code.
output.types
, and specifically its error
key, allows library authors to override the return type of operation functions.
It's fairly common for client libraries to create their own error that acts as a superset for all of the errors defined in the OpenAPI description.
This provides a consistent interface for client library consumers.
Much of the configuration of this code generator relates to the naming and output of schemas. Remember that the library takes an operation-first mindset, and will not output schemas that are not actively used by an operation.
Following is an example operation module that has been annotated with the configuration options involved in various parts of the rendered output.
# Existence: `ignore`
# Location: `output.location`, `output.schema_subdirectory`
# Module name: `output.base_module`
defmodule Example.MySchema do
@moduledoc """
Provides struct and types for a MySchema
"""
# `output_schema_use`
use Example.Helper
@type t :: %__MODULE__{
example: String.t()
}
defstruct [
# output.extra_fields
:__extra__,
:example
]
@doc false
@spec __fields__(atom) :: keyword
def __fields__(type \\ :t)
def __fields__(:t) do
[
example: {:string, :generic}
]
end
end
Below are some of the highlights from the available configuration. See the Option Reference section for an exhaustive list of the available options.
Schemas can be renamed, grouped, and merged. Applying variations of these options to the API description can turn generated code from confusing to ergonomic rather quickly.
Learn more in the documentation for OpenAPI.Processor.Naming.schema_module_and_type/2
.
The ignore
option allows operations to be ignored based on their Operation IDs or paths in the API description.
Patterns can be exact strings or regular expressions.
Learn more in the documentation for OpenAPI.Processor.Ignore
.
output.extra_fields
allows authors to insert additional fields into every outputted schema.
For example, the GitHub library adds an __info__
map to each schema.
Like the __meta__
field provided by Ecto.Schema
or the private
field on Plug.Conn
, this field can hold additional library-specific information without interfering with the API's own data.
output.schema_use
allows authors to insert a use [output.schema_use]
statement at the top of every module containing a schema.
A corresponding module must be supplied by the library with a __using__/1
macro.
Inside this macro, library authors can inject additional code that may be helpful.
For example:
defmodule MyLibrary.Schema do
defmacro __using__(_opts) do
quote do
@derive {Jason.Encoder, except: [:__info__]}
end
end
end
Inside the macro, you can reference the name of the current module using __MODULE__
, along with any other variables that are part of the macro environment.
Below are all of the configuration options offered by the default implementations of the processor and renderer.
They are expressed in terms of nested keyword lists.
For example, output.base_module
represents the following:
config :oapi_generator, default: [
output [
base_module: ...
]
]
Remember that all configuration values must be contained within a profile.
-
ignore
: List of patterns (string or regular expression) representing operations or schemas to ignore during code generation. Defaults to an empty list of patterns. SeeOpenAPI.Processor.Ignore
for more information. -
naming.default_operation_module
: Module into which operation functions will be rendered if a module cannot be determined based on their operation ID or tags. The configuration should not include the base module configured inoutput.base_module
. Defaults toOperations
. SeeOpenAPI.Processor.Naming.operation_modules/2
for more information. -
naming.field_casing
: Either:camel
,:snake
, ornil
(default) to output schema field names ascamelCase
,snake_case
, or leave the fields name as-is from the API description. Changing the field casing is likely to be a breaking change for clients, unless the API description consistently uses the same casing. Setting this field may be necessary if field names require normalization (ex. if a field begins with a number). -
naming.group
: List of module namespaces to use while naming operations and schemas. Defaults to an empty list of modules. SeeOpenAPI.Processor.Naming.group_schema/2
for more information. -
naming.merge
: List of tuples containing patterns and replacements to use when merging schema modules. Defaults to an empty list of merges. SeeOpenAPI.Processor.Naming.merge_schema/2
for more information. -
naming.operation_use_tags
: Whether to use tags when determining module names for operations. Defaults totrue
. SeeOpenAPI.Processor.Naming.operation_modules/2
for more information. -
naming.rename
: List of tuples containing patterns and replacements to use when renaming modules. Defaults to an empty list of replacements. SeeOpenAPI.Processor.Naming.rename_schema/2
for more information. -
output.base_module
: Base module of the generated code. This is often the name of the library you intend to create, and it acts as a prefix for all rendered modules. Defaults tonil
, or no module prefix. SeeOpenAPI.Renderer.Module.render/2
for more information. -
output.default_client
: Default client to use in operation modules. Every operation function calls a dynamic module to perform requests, and this configuration determines the default value. Defaults to[output.base_module].Client
. SeeOpenAPI.Renderer.Module.render_default_client/2
for more information. -
output.extra_fields
: Additional fields to add to all schema struct definitions and their typespecs, expressed as a keyword list of types. This can be useful for private data the library wishes to add to all outputted data. Defaults to an empty list of fields. SeeOpenAPI.Renderer.Schema
for more information. -
output.location
: Base filesystem location for all rendered files. This is often a directory likelib
to follow Elixir conventions. Defaults to the current working directory. SeeOpenAPI.Renderer.Module.filename/2
for more information. -
output.operation_call.request
: Format of therequest
key in the body of an operation function. By default, the possible request body schemas are formatted as a:list
of tuples with the content type and the schema. A value of:map
will cause a map to be output instead. SeeOpenAPI.Renderer.Operation.render_function/2
for more information. -
output.operation_subdirectory
: Subdirectory to use for all rendered files containing operations. This is an optional way to colocate generated operation modules in a single directory away from other parts of the client library. Defaults to the same location asoutput.location
. SeeOpenAPI.Renderer.Module.filename/2
for more information. -
output.operation_use
: Module to include as ause
statement at the top of every module containing operations. Defaults to no used module. SeeOpenAPI.Renderer.Module.render_using/2
for more information. -
output.schema_subdirectory
: Subdirectory to use for all rendered files containing only schemas. This is an optional way to colocate generated schema modules in a single directory away from other parts of the client library. Defaults to the same location asoutput.location
. SeeOpenAPI.Renderer.Module.filename/2
for more information. -
output.schema_use
: Module to include as ause
statement at the top of every module containing only schemas. Defaults to no used module. SeeOpenAPI.Renderer.Module.render_using/2
for more information. -
output.types.error
: Type to override the error return type of all operation functions. This is useful when the client intends to normalize errors from the API. By default, the error type is a union of all possible error responses for the operation. SeeOpenAPI.Renderer.Operation.render_spec/2
for more information. -
reader.additional_files
: List of paths to supplemental root files of an API description. Defaults to an empty list. SeeOpenAPI.Reader
for more information. -
reader.file
: Path to the root file of an API description. This option is often supplied to themix api.gen
mix task instead. SeeOpenAPI.Reader
for more information.