AshOaskit.Spec behaviour (AshOasKit v0.2.1)

View Source

Runtime support and behaviour for spec modules defined with use AshOaskit.

A spec module turns a set of Ash domains into a long-lived OpenAPI spec that integrates with the whole Oaskit toolchain:

defmodule MyAppWeb.ApiSpec do
  use AshOaskit,
    domains: [MyApp.Blog],
    title: "My API",
    api_version: "1.0.0"
end

use AshOaskit implements the Oaskit behaviour, so the module gains, with no extra code:

  • Caching — the generated spec is stored in :persistent_term and the Ash domain walk runs once, not per request
  • ServingOaskit.SpecController can serve it as JSON or render a Redoc UI
  • Request validationOaskit.Plugs.SpecProvider + Oaskit.Plugs.ValidateRequest work for hand-written Phoenix controllers documented alongside the Ash routes
  • Exportmix openapi.dump MyAppWeb.ApiSpec

Options

OptionTypeDefaultDescription
:domains[module()]requiredAsh domains to include
:version"3.0" or "3.1""3.1"OpenAPI version
:titleString.t()"API"info.title
:api_versionString.t()"1.0.0"info.version
:descriptionString.t()nilinfo.description
:terms_of_serviceString.t()nilinfo.termsOfService
:contactmap()nilinfo.contact
:licensemap()nilinfo.license
:servers[String.t() | map()][]servers array
:security[map()]nilTop-level security requirements
:external_docsmap()nilExternal documentation object
:routermodule()nilPhoenix router for controller introspection
:modify_open_apifunction or MFAnilPost-generation hook (see AshOaskit.SpecModifier)
:spec_buildermodule()nilAshOaskit.SpecBuilder implementation
:cacheboolean()trueCache the generated spec

Customizing the spec

Override modify_spec/1 to post-process the generated map (the result is what gets cached):

defmodule MyAppWeb.ApiSpec do
  use AshOaskit, domains: [MyApp.Blog]

  @impl AshOaskit.Spec
  def modify_spec(spec) do
    put_in(spec, ["components", "securitySchemes"], %{
      "bearerAuth" => %{"type" => "http", "scheme" => "bearer"}
    })
  end
end

All Oaskit callbacks remain overridable too — cache/1 to swap the cache backend, cache_variant/0 to key the cache (e.g. per tenant), and jsv_opts/0 for validation tuning.

Caching

The spec is generated once and cached in :persistent_term under {:ash_oaskit_cache, module, cache_variant}. Two switches disable it:

  • per module: use AshOaskit, cache: false, ...
  • globally at runtime: config :ash_oaskit, cache_specs: false (recommended in dev.exs so code reloads regenerate the spec)

Dual-version output

An Oaskit spec module is one spec by contract. To serve OpenAPI 3.0 and 3.1 side by side, define two spec modules with different :version options.

Summary

Types

Compile-time options accepted by use AshOaskit.

Callbacks

Invoked after the spec is generated, before it is cached.

Functions

Builds (and caches) the OpenAPI spec for a spec module.

Validates use AshOaskit options at compile time.

Types

option()

@type option() ::
  {:domains, [module()]}
  | {:version, String.t()}
  | {:title, String.t()}
  | {:api_version, String.t()}
  | {:description, String.t()}
  | {:terms_of_service, String.t()}
  | {:contact, map()}
  | {:license, map()}
  | {:servers, [String.t() | map()]}
  | {:security, [map()]}
  | {:external_docs, map()}
  | {:router, module()}
  | {:modify_open_api, function() | {module(), atom(), [term()]}}
  | {:spec_builder, module()}
  | {:cache, boolean()}

Compile-time options accepted by use AshOaskit.

Callbacks

modify_spec(spec)

@callback modify_spec(spec :: map()) :: map()

Invoked after the spec is generated, before it is cached.

Override to add security schemes, vendor extensions, or any other post-processing. Receives and must return the spec map.

Functions

build(module, opts)

@spec build(module(), [option()]) :: map()

Builds (and caches) the OpenAPI spec for a spec module.

Used by the spec/0 callback that use AshOaskit defines. The cache is keyed on the module and its cache_variant/0, and can be disabled per module (cache: false) or globally (config :ash_oaskit, cache_specs: false).

Parameters

  • module - The spec module (use AshOaskit)
  • opts - The validated use AshOaskit options

Returns

The OpenAPI specification map.

validate_opts!(opts, module)

@spec validate_opts!([option()], module()) :: [option()]

Validates use AshOaskit options at compile time.

Raises ArgumentError for unknown options, a missing or empty :domains list, or an unsupported :version.

Examples

iex> AshOaskit.Spec.validate_opts!([domains: [MyApp.Blog]], MyModule)
[domains: [MyApp.Blog]]