AshOaskit (AshOasKit v0.2.1)
View SourceOpenAPI specification generator for Ash Framework domains.
AshOaskit automatically generates OpenAPI 3.0 and 3.1 specifications from your Ash domains by introspecting resources, attributes, actions, and AshJsonApi routes.
Spec Modules (Recommended)
Define a spec module with use AshOaskit to get a cached, oaskit-native
spec that plugs into the whole Oaskit toolchain:
defmodule MyAppWeb.ApiSpec do
use AshOaskit,
domains: [MyApp.Blog, MyApp.Accounts],
title: "My API",
api_version: "1.0.0"
endThe module implements the Oaskit behaviour, which means:
# Serve it (JSON + Redoc UI) straight from your Phoenix router
get "/openapi.json", Oaskit.SpecController, spec: MyAppWeb.ApiSpec
get "/redoc", Oaskit.SpecController, redoc: "/openapi.json"
# Or via the AshOaskit router macro
use AshOaskit.Router,
spec: MyAppWeb.ApiSpec,
open_api: "/openapi",
redoc: "/redoc"
# Export from the command line
mix openapi.dump MyAppWeb.ApiSpec
# Validate requests in hand-written controllers documented
# alongside the Ash routes
plug Oaskit.Plugs.SpecProvider, spec: MyAppWeb.ApiSpecThe generated spec is cached in :persistent_term — the Ash domain
walk runs once, not on every request. See AshOaskit.Spec for all
options, the modify_spec/1 customization callback, and cache
controls.
Other Levels of Customization
1. Router Macro (Simple)
For quick setup with standard endpoints:
# In your Phoenix router
scope "/api" do
pipe_through :api
use AshOaskit.Router,
domains: [MyApp.Blog, MyApp.Accounts],
open_api: "/docs/openapi",
title: "My API",
version: "1.0.0"
endThis automatically generates routes for both OpenAPI 3.0 and 3.1:
GET /api/docs/openapi.json -> Default (3.1) spec
GET /api/docs/openapi/3.0.json -> OpenAPI 3.0 spec
GET /api/docs/openapi/3.1.json -> OpenAPI 3.1 spec2. Router Macro + Custom SpecBuilder
For customization (security schemes, feature flags, domain filtering):
use AshOaskit.Router,
spec_builder: MyApp.OpenApi.SpecBuilder,
domains: [MyApp.Blog],
open_api: "/openapi",
title: "My API"Where MyApp.OpenApi.SpecBuilder implements AshOaskit.SpecBuilder:
defmodule MyApp.OpenApi.SpecBuilder do
@behaviour AshOaskit.SpecBuilder
@impl true
def spec(openapi_version, opts) do
AshOaskit.spec(
domains: opts[:domains],
version: openapi_version,
title: opts[:title]
)
|> add_security_schemes()
|> add_feature_flags()
end
defp add_security_schemes(spec) do
put_in(spec, ["components", "securitySchemes"], %{
"bearerAuth" => %{
"type" => "http",
"scheme" => "bearer",
"bearerFormat" => "JWT"
}
})
end
defp add_feature_flags(spec) do
Map.put(spec, "x-features", %{"beta" => true})
end
end3. Programmatic API (Advanced)
For complete control over routing and serving:
# Generate spec programmatically in your own controller
spec = AshOaskit.spec(
domains: [MyApp.Blog],
version: "3.1",
title: "My API"
)
|> add_custom_processing()This is an escape hatch for edge cases not covered by the Router macro.
When to Use Which
| Use Case | Approach |
|---|---|
| Standard API documentation | Spec module (use AshOaskit) |
| Custom security schemes | Spec module + modify_spec/1 |
| Feature flags / extensions | Spec module + modify_spec/1 |
Redoc UI / request validation / mix openapi.dump | Spec module |
| Version-specific domains | Two spec modules |
| Non-standard routing | Programmatic API |
| Complete custom control | Programmatic API |
Architecture Overview
┌────────────────┐
│ Ash Domain │
│ (Resources) │
└───────┬────────┘
│
▼
┌───────────────┐ ┌─────────────────┐
│ Config │─────▶│ TypeMapper │
│ (metadata) │ │ (Ash -> JSON) │
└───────┬───────┘ └────────┬────────┘
│ │
▼ ▼
┌───────────────┐ ┌─────────────────┐
│ SchemaBuilder │◀─────│ FilterBuilder │
│ (components) │ │ SortBuilder │
└───────┬───────┘ │ QueryParams │
│ └─────────────────┘
▼
┌───────────────┐ ┌─────────────────┐
│ Generator │─────▶│ ResponseLinks │
│ (v30/v31) │ │ ResponseMeta │
└───────┬───────┘ │ ErrorSchemas │
│ └─────────────────┘
▼
┌───────────────┐
│ OpenAPI Spec │
│ (JSON/YAML) │
└───────────────┘Features
- Dual Version Support - Generate OpenAPI 3.0 or 3.1 specifications
- Automatic Schema Generation - Extracts schemas from Ash resource attributes
- AshJsonApi Integration - Builds paths from AshJsonApi routes when available
- JSON:API Compliance - Generates proper JSON:API document structures
- Type Mapping - Comprehensive Ash type to JSON Schema conversion
- Filter Support - Generates filter parameter schemas from resource filters
- Sort Support - Generates sort parameter schemas from resource sorts
- Pagination - Handles offset, keyset, and cursor pagination styles
- Relationships - Proper handling of belongs_to, has_many, has_one
- Router Macro - Quick setup for serving specs in Phoenix
- SpecBuilder Behaviour - Customizable spec generation via behaviour
- Plug Controller - Flexible controller for custom configurations
- Mix Task - Generate spec files from the command line
Quick Start
# Generate OpenAPI 3.1 spec (default)
spec = AshOaskit.spec(domains: [MyApp.Blog])
# Generate OpenAPI 3.0 spec
spec = AshOaskit.spec_30(domains: [MyApp.Blog])
# With full options
spec = AshOaskit.spec(
domains: [MyApp.Blog, MyApp.Accounts],
title: "My API",
api_version: "2.0.0",
description: "Blog and Accounts API",
servers: ["https://api.example.com"]
)Options
All functions accept these common options:
:domains- List of Ash domains to include (required):version- OpenAPI version:"3.0"or"3.1"(default:"3.1"):title- API title (default:"API"):api_version- API version string (default:"1.0.0"):description- API description:servers- List of server URLs or server objects:contact- Contact information map with:name,:email,:url:license- License information map with:name,:url:terms_of_service- Terms of service URL:security- Security requirements list:external_docs- External documentation map
Configuration
Set defaults in your application config:
config :ash_oaskit,
version: "3.1",
title: "My API",
api_version: "1.0.0",
domains: [MyApp.Blog, MyApp.Accounts]Mix Task
Generate spec files from the command line:
# Generate JSON spec
mix ash_oaskit.generate --domains MyApp.Blog --output openapi.json
# Generate YAML spec with options
mix ash_oaskit.generate \
--domains MyApp.Blog,MyApp.Accounts \
--output openapi.yaml \
--title "My API" \
--version 3.0Module Overview
| Module | Purpose |
|---|---|
AshOaskit.Spec | Spec module behaviour and runtime (use AshOaskit) |
AshOaskit.Router | Router macro for quick Phoenix integration |
AshOaskit.SpecBuilder | Behaviour for custom spec generation |
AshOaskit.SpecBuilder.Default | Default SpecBuilder implementation |
AshOaskit.Controller | Plug controller for serving specs |
AshOaskit.OpenApi | Main entry point for spec generation |
AshOaskit.Config | Configuration and domain introspection |
AshOaskit.TypeMapper | Ash type to JSON Schema mapping |
See individual module documentation for detailed information.
Summary
Functions
Defines an oaskit-compatible spec module from Ash domains.
Generate an OpenAPI specification for the given domains.
Generate an OpenAPI 3.0 specification.
Generate an OpenAPI 3.1 specification.
Validate an OpenAPI specification through Oaskit.
Validate an OpenAPI specification through Oaskit.
Functions
Defines an oaskit-compatible spec module from Ash domains.
Implements the Oaskit behaviour with a spec/0 that generates (and
caches) the OpenAPI spec for the configured domains, plus a default
passthrough AshOaskit.Spec.modify_spec/1. Both are overridable,
as are Oaskit's cache/1, cache_variant/0, and jsv_opts/0.
See AshOaskit.Spec for the full option reference and examples.
Examples
defmodule MyAppWeb.ApiSpec do
use AshOaskit,
domains: [MyApp.Blog],
title: "My API",
api_version: "1.0.0"
end
MyAppWeb.ApiSpec.spec()
# => %{"openapi" => "3.1.0", ...}
Generate an OpenAPI specification for the given domains.
This is the main entry point for generating OpenAPI specifications. It delegates
to AshOaskit.OpenApi.spec/1 and supports both OpenAPI 3.0 and 3.1 versions.
Options
:domains- List of Ash domains to include (required):version- OpenAPI version: "3.0" or "3.1" (default: "3.1"):title- API title (default: "API"):api_version- API version string (default: "1.0.0"):servers- List of server URLs:description- API description
Examples
iex> spec = AshOaskit.spec(domains: [AshOaskit.Test.Blog])
...> spec["openapi"]
"3.1.0"
iex> spec = AshOaskit.spec(domains: [AshOaskit.Test.Blog], version: "3.0")
...> spec["openapi"]
"3.0.3"
iex> spec = AshOaskit.spec(domains: [AshOaskit.Test.Blog], title: "My API")
...> spec["info"]["title"]
"My API"Return Value
Returns a map representing the complete OpenAPI specification that can be encoded to JSON or YAML.
Generate an OpenAPI 3.0 specification.
Convenience function that calls spec/1 with version: "3.0".
OpenAPI 3.0 uses nullable: true for optional fields and has some
differences in how types are represented compared to 3.1.
Examples
iex> spec = AshOaskit.spec_30(domains: [AshOaskit.Test.Blog])
...> spec["openapi"]
"3.0.3"See spec/1 for full options documentation.
Generate an OpenAPI 3.1 specification.
Convenience function that calls spec/1 with version: "3.1".
OpenAPI 3.1 aligns more closely with JSON Schema and uses
type: ["string", "null"] for nullable fields.
Examples
iex> spec = AshOaskit.spec_31(domains: [AshOaskit.Test.Blog])
...> spec["openapi"]
"3.1.0"See spec/1 for full options documentation.
Validate an OpenAPI specification through Oaskit.
Returns {:ok, %Oaskit.Spec.OpenAPI{}} on success or {:error, error} on failure.
Examples
spec = AshOaskit.spec(domains: [MyApp.Blog])
{:ok, validated} = AshOaskit.validate(spec)
Validate an OpenAPI specification through Oaskit.
Returns %Oaskit.Spec.OpenAPI{} on success or raises on failure.
Examples
spec = AshOaskit.spec(domains: [MyApp.Blog])
validated = AshOaskit.validate!(spec)