View Source Open API

Use with Phoenix

To set up the Open API endpoints for your application, first include the :open_api_spex dependency:

{:open_api_spex, "~> 3.16"},

Then in the module where you call use AshJsonApi.Router add the following option:

use AshJsonApi.Router, domains: [...], open_api: "/open_api"

Finally, you can use utilities provided by open_api_spex to show UIs for your API. Be sure to put your forward call last, if you are putting your API at a sub-path.

forward "/api/swaggerui",
  OpenApiSpex.Plug.SwaggerUI,
  path: "/api/open_api",
  default_model_expand_depth: 4

forward "/api/redoc",
  Redoc.Plug.RedocUI,
  spec_url: "/api/open_api"

forward "/api", YourApp.YourApiRouter

Now you can go to /api/swaggerui and /api/redoc!

Use with Plug

To set up the open API endpoints for your application, first include the :open_api_spex and :redoc_ui_plug dependency:

{:open_api_spex, "~> 3.16"},
{:redoc_ui_plug, "~> 0.2.1"},

Then in the module where you call use AshJsonApi.Router add the following option:

use AshJsonApi.Router, domains: [...], open_api: "/open_api"

Finally, you can use utilities provided by open_api_spex to show UIs for your API. Be sure to put your forward call last, if you are putting your API at a sub-path.

forward "/api/swaggerui",
  to: OpenApiSpex.Plug.SwaggerUI,
  init_opts: [
    path: "/api/open_api",
    default_model_expand_depth: 4
  ]

forward "/api/redoc",
  to: Redoc.Plug.RedocUI,
  init_opts: [
    spec_url: "/api/open_api"
  ]

forward "/api", YourApp.YourApiRouter

Now you can go to /api/swaggerui and /api/redoc!

Customize values in the OpenAPI documentation

To customize the main values of the OpenAPI spec, a few options are available:

  use AshJsonApi.Router,
    domains: [...],
    open_api: "/open_api",
    open_api_title: "Title",
    open_api_version: "1.0.0",
    open_api_servers: ["http://domain.com/api/v1"]

If :open_api_servers is not specified, a default server is automatically derived from your app's Phoenix endpoint, as retrieved from inbound connections on the open_api HTTP route.

In case an active connection is not available, for example when generating the OpenAPI spec via CLI, you can explicitely specify a reference to the Phoenix endpoint:

  use AshJsonApi.Router,
    domains: [...],
    open_api: "/open_api",
    phoenix_endpoint: MyAppWeb.Endpoint

To override any value in the OpenApi documentation you can use the :modify_open_api options key:

  use AshJsonApi.Router,
    domains: [...],
    open_api: "/open_api",
    modify_open_api: {__MODULE__, :modify_open_api, []}

  def modify_open_api(spec, _, _) do
    %{
      spec
      | info: %{spec.info | title: "MyApp Title JSON API", version: Application.spec(:my_app, :vsn) |> to_string()}
    }
  end

Generate spec files via CLI

You can write the OpenAPI spec file to disk using the Mix tasks provided by OpenApiSpex.

Supposing you have setup AshJsonApi as:

defmodule MyAppWeb.AshJsonApi
  use AshJsonApi.Router, domains: [...], open_api: "/open_api"
end

you can generate the files with:

mix openapi.spec.json --spec MyAppWeb.AshJsonApi
mix openapi.spec.yaml --spec MyAppWeb.AshJsonApi

Setting a route prefix for generated files

The route prefix in normal usage is automatically inferred, but when generating files we will use the prefix option set in the json_api section of the relevant Ash.Domain module.

To generate the YAML file you need to add the ymlr dependency.

def deps do
  [
    {:ymlr, "~> 2.0"}
  ]
end

You can also use the --check option to confirm that your checked in spec file(s) match.

mix openapi.spec.json --spec MyAppWeb.AshJsonApiRouter --check
mix openapi.spec.yaml --spec MyAppWeb.AshJsonApiRouter --check

Using this file in production

To avoid generating the spec every time your open_api endpoint is hit, you can use the open_api_file option. Ensure that it points to an existing .json file. You will almost certainly want to do this only for production so that the schema is generated dynamically in dev, but served statically in production.

open_api_file =
  if Mix.env() == :prod do
    "priv/static/open_api.json"
  else
    nil
  end

use AshJsonApi.Router,
  domains: [...],
  open_api: "/open_api",
  modify_open_api: {__MODULE__, :modify_open_api, []},
  open_api_file: open_api_file

Known issues/limitations

Swagger UI

SwaggerUI does not properly render recursive types. This affects the examples and type documentation for the filter parameter especially.

Redoc

Redoc does not show all available schemas in the sidebar. This means that some schemas that are referenced only but have no endpoints that refer to them are effectively un-discoverable without downloading the spec and hunting them down yourself.