Spectral.OpenAPI (Spectral v0.17.0)

Copy Markdown View Source

Elixir wrapper for spectra OpenAPI specification generation.

This module provides idiomatic Elixir functions for generating OpenAPI 3.1 specifications from Elixir type definitions using spectra.

Response Builder Pattern

Responses are built using a fluent API:

response = Spectral.OpenAPI.response(200, "Success")
  |> Spectral.OpenAPI.response_with_body(Person, :t)
  |> Spectral.OpenAPI.response_with_header("X-Rate-Limit", RateLimit, %{
    schema: :t,
    description: "Remaining requests",
    required: false
  })

endpoint = Spectral.OpenAPI.endpoint(:get, "/users/{id}")
  |> Spectral.OpenAPI.add_response(response)

Summary

Types

An endpoint or a webhook. The builder functions accept both.

Functions

Adds a complete response specification to an endpoint.

Creates a new OpenAPI endpoint definition.

Creates a new OpenAPI endpoint definition using metadata from a spectral/1 macro placed before a function definition.

Converts a list of endpoints to a complete OpenAPI specification.

Converts a list of endpoints to a complete OpenAPI specification, with options.

Creates a response builder.

Adds a response body to a response builder.

Converts endpoints and webhooks to a complete OpenAPI specification.

Creates a new OpenAPI 3.1 webhook definition.

Marks a webhook whose payload is encoded with nil_fields: :null.

Adds a parameter to an endpoint.

Adds a request body specification to an endpoint.

Types

operation()

An endpoint or a webhook. The builder functions accept both.

Functions

add_response(endpoint, response)

@spec add_response(operation(), :spectra_openapi.response_spec()) :: operation()

Adds a complete response specification to an endpoint.

This function adds a response that was built using the response builder pattern.

Parameters

  • endpoint - The endpoint to add the response to
  • response - Response specification built with response/2 and related functions

Returns

  • endpoint - Updated endpoint with the response added

Example

response = Spectral.OpenAPI.response(200, "User found")
  |> Spectral.OpenAPI.response_with_body(Person, :t)

endpoint = Spectral.OpenAPI.endpoint(:get, "/users/{id}")
  |> Spectral.OpenAPI.add_response(response)

endpoint(method, path, doc \\ %{})

Creates a new OpenAPI endpoint definition.

Parameters

  • method - HTTP method as an atom (:get, :post, :put, :delete, :patch, etc.)
  • path - URL path as a binary (e.g., "/users/{id}")
  • doc - Optional documentation map with optional keys:
    • :summary - Short summary of the endpoint
    • :description - Longer description of the endpoint
    • :operationId - Unique string to identify the operation
    • :tags - List of tags for grouping endpoints
    • :deprecated - Whether the endpoint is deprecated (boolean)
    • :externalDocs - Map with :url (required) and :description (optional)
    • :security - List of Security Requirement Objects for this operation alone, overriding the global :security default. [] opts the operation out of the global requirement entirely.

Returns

  • endpoint - OpenAPI endpoint structure

Example

iex> endpoint = Spectral.OpenAPI.endpoint(:get, "/users/{id}", %{summary: "Get user by ID"})
iex> endpoint.doc
%{summary: "Get user by ID"}

endpoint(method, path, module, function_name, arity)

Creates a new OpenAPI endpoint definition using metadata from a spectral/1 macro placed before a function definition.

The function's metadata (set via spectral summary: "...", description: "..." before the corresponding @spec) is retrieved from the module's type info and used as the endpoint documentation.

Parameters

  • method - HTTP method as an atom (:get, :post, :put, :delete, :patch, etc.)
  • path - URL path as a binary (e.g., "/users/{id}")
  • module - The module containing the function with spectral metadata
  • function_name - The function name as an atom
  • arity - The function arity

Returns

  • endpoint - OpenAPI endpoint structure with documentation from the function's metadata

Example

defmodule MyController do
  use Spectral

  spectral summary: "Get user", description: "Returns a user by ID"
  @spec get_user(map(), map()) :: map()
  def get_user(_conn, _params), do: %{}
end

endpoint = Spectral.OpenAPI.endpoint(:get, "/users/{id}", MyController, :get_user, 2)

endpoints_to_openapi(metadata, endpoints)

@spec endpoints_to_openapi(:spectra_openapi.openapi_metadata(), [
  :spectra_openapi.endpoint_spec()
]) ::
  {:ok, iodata()} | {:error, [Spectral.Error.t()]}

Converts a list of endpoints to a complete OpenAPI specification.

Parameters

  • metadata - OpenAPI metadata map with keys:
    • :title - API title (required)
    • :version - API version (required)
    • :summary (optional) - Short summary of the API
    • :description (optional) - Longer description of the API
    • :terms_of_service (optional) - URL to the terms of service
    • :contact (optional) - Contact map with optional :name, :url, :email
    • :license (optional) - License map with required :name and optional :url, :identifier
    • :servers (optional) - List of server objects, each with required :url and optional :description
  • endpoints - List of endpoint definitions

Returns

  • {:ok, iodata} - Complete OpenAPI 3.1 specification serialised as JSON iodata
  • {:error, [%Spectral.Error{}]} - List of errors if generation fails

Example

metadata = %{title: "My API", version: "1.0.0"}
endpoints = [
  Spectral.OpenAPI.endpoint(:get, "/users/{id}")
  |> Spectral.OpenAPI.add_response(
    Spectral.OpenAPI.response(200, "User found")
    |> Spectral.OpenAPI.response_with_body(Person, :t)
  )
]

{:ok, json} = Spectral.OpenAPI.endpoints_to_openapi(metadata, endpoints)

endpoints_to_openapi(metadata, endpoints, opts)

@spec endpoints_to_openapi(
  :spectra_openapi.openapi_metadata(),
  [:spectra_openapi.endpoint_spec()],
  [
    Spectral.schema_option()
  ]
) :: {:ok, iodata() | dynamic()} | {:error, [Spectral.Error.t()]}

Converts a list of endpoints to a complete OpenAPI specification, with options.

Like endpoints_to_openapi/2 but accepts an options list.

Parameters

  • metadata - OpenAPI metadata map (same as endpoints_to_openapi/2)
  • endpoints - List of endpoint definitions
  • opts - Options list. Supported options:
    • :pre_encoded - Return a map instead of iodata, skipping JSON encoding.

Returns

  • {:ok, iodata()} - Complete OpenAPI 3.1 spec as JSON iodata (default)
  • {:ok, map()} - Spec as a decoded map when :pre_encoded option is set
  • {:error, [%Spectral.Error{}]} - List of errors if generation fails

response(status_code, description)

Creates a response builder.

This creates a response specification that can be further configured with response_with_body/3-4 and response_with_header/4 before being added to an endpoint with add_response/2.

Parameters

  • status_code - HTTP status code (e.g., 200, 404, 500), or an OpenAPI status code range from "1XX" to "5XX"
  • description - Human-readable description of the response

Returns

  • response - Response builder structure

Example

response = Spectral.OpenAPI.response(200, "User found successfully")

response_with_body(response, module, schema)

Adds a response body to a response builder.

You can optionally provide a content_type as a 4th argument.

Parameters

  • response - Response builder from response/2
  • module - Module containing the type definition
  • schema - Schema reference (typically an atom like :t)
  • content_type - Optional content type (e.g., "application/json", "application/xml")

Returns

  • response - Updated response builder with body schema

Example

response = Spectral.OpenAPI.response(200, "Success")
  |> Spectral.OpenAPI.response_with_body(Person, :t)

response_xml = Spectral.OpenAPI.response(200, "Success")
  |> Spectral.OpenAPI.response_with_body(Person, :t, "application/xml")

response_with_header(response, header_name, module, header_spec)

@spec response_with_header(
  :spectra_openapi.response_spec(),
  binary(),
  module(),
  map()
) ::
  :spectra_openapi.response_spec()

Adds a header to a response builder.

Parameters

  • response - Response builder from response/2
  • header_name - Name of the response header (e.g., "X-Rate-Limit")
  • module - Module containing the type definition for the header value
  • header_spec - Header specification map with keys:
    • :schema - Schema reference for the header value (atom like :t, or a type ref tuple)
    • :description (optional) - Description of the header
    • :required (optional) - Whether the header is required (default: false)
    • :deprecated (optional) - Whether the header is deprecated (boolean)

Returns

  • response - Updated response builder with header added

Example

response = Spectral.OpenAPI.response(200, "Success")
  |> Spectral.OpenAPI.response_with_header("X-Rate-Limit", RateLimit, %{
    schema: :t,
    description: "Requests remaining",
    required: false
  })

to_openapi(metadata, endpoints, webhooks, opts)

Converts endpoints and webhooks to a complete OpenAPI specification.

Like endpoints_to_openapi/3, but also takes webhooks built with webhook/2,3. They are emitted under the spec's top-level webhooks key, and their schemas share components/schemas with the endpoints, so a type used by both is emitted once.

A global :security requirement in the metadata applies to webhooks too. Override it per operation with :security in the doc map, or [] to opt out.

Parameters

  • metadata - OpenAPI metadata map (same as endpoints_to_openapi/2)
  • endpoints - List of endpoint definitions
  • webhooks - List of webhook definitions built with webhook/2,3
  • opts - Options list. Supported options:
    • :pre_encoded - Return a map instead of iodata, skipping JSON encoding.

Returns

  • {:ok, iodata()} - Complete OpenAPI 3.1 spec as JSON iodata (default)
  • {:ok, map()} - Spec as a decoded map when :pre_encoded option is set
  • {:error, [%Spectral.Error{}]} - List of errors if generation fails

webhook(name, method, doc \\ %{})

Creates a new OpenAPI 3.1 webhook definition.

A webhook is keyed by an event name and emitted under the spec's top-level webhooks key by to_openapi/4. Build it with the same functions as an endpoint. Only :header and :cookie parameters are allowed.

Parameters

  • name - Event name as a binary (e.g., "userCreated")
  • method - HTTP method your API uses when calling the consumer (usually :post)
  • doc - Optional documentation map, taking the same keys as endpoint/3

Returns

  • webhook - OpenAPI webhook structure

Example

iex> webhook = Spectral.OpenAPI.webhook("userCreated", :post, %{summary: "User created"})
iex> webhook.name
"userCreated"

with_nil_fields(webhook, nil_fields)

@spec with_nil_fields(:spectra_openapi.webhook_spec(), :null) ::
  :spectra_openapi.webhook_spec()

Marks a webhook whose payload is encoded with nil_fields: :null.

Use it when the code that sends the webhook calls Spectral.encode/5 with nil_fields: :null. The payload schema then lists every field whose type can be nil in required and allows null, nested types included. Headers and responses do not change.

The payload type keeps its normal schema name. If the same type is also used without this option, the webhook gets a second schema with Nullable added to the name. With config :spectra, :nil_fields, :null the option changes nothing. With :omit, to_openapi/4 raises ArgumentError, and so it does when the Nullable name is already taken. Any value other than :null raises ArgumentError.

Example

webhook = Spectral.OpenAPI.webhook("paymentDone", :post)
  |> Spectral.OpenAPI.with_request_body(Payment, {:type, :t, 0})
  |> Spectral.OpenAPI.with_nil_fields(:null)

with_parameter(endpoint, module, parameter_spec)

Adds a parameter to an endpoint.

Parameters can be in the path, query string, headers, or cookies.

Parameters

  • endpoint - The endpoint or webhook to modify
  • module - Module containing the type definition for the parameter
  • parameter_spec - Parameter specification map with keys:
    • :name - Parameter name
    • :in - Location (:path, :query, :header, :cookie)
    • :required - Whether the parameter is required
    • :schema - Schema for the parameter value (atom or {:type, name, arity} tuple)

The description and deprecated fields in the rendered OpenAPI output are sourced from the type's spectral annotation, not from the parameter spec. Type aliases and remote type references are followed automatically to inherit their documentation.

Returns

  • endpoint - Modified endpoint with parameter added

Example

endpoint = Spectral.OpenAPI.endpoint(:get, "/users/{id}")
  |> Spectral.OpenAPI.with_parameter(User, %{
    name: "id",
    in: :path,
    required: true,
    schema: :string
  })

with_request_body(endpoint, module, schema)

@spec with_request_body(operation(), module(), atom() | :spectra.sp_type_or_ref()) ::
  operation()

Adds a request body specification to an endpoint.

You can optionally provide a content_type as a 4th argument.

Parameters

  • endpoint - The endpoint or webhook to modify
  • module - Module containing type definitions
  • schema - Schema reference (typically an atom like :t)
  • content_type - Optional content type binary (e.g., "application/xml"; defaults to "application/json")

Returns

  • endpoint - Modified endpoint with request body

Example

endpoint = Spectral.OpenAPI.endpoint(:post, "/users")
  |> Spectral.OpenAPI.with_request_body(Person, :t)

endpoint_xml = Spectral.OpenAPI.endpoint(:post, "/users")
  |> Spectral.OpenAPI.with_request_body(Person, :t, "application/xml")