I dunno… I just went to Kagi Assistant with the prompt:
What is the best way to define OpenAPI specs in Elixir?
and this was the response…
I don’t know if what it had to say was correct or if the results were influenced by this being a search engine assistant vs. a more conventional coding tool. But it definitely discussed both those libraries and that was my first (and last) try to ask about that subject.
I would say the whole area of LLM capability, weakness, and utility doesn’t lend itself well to simple anecdotes for any practical purpose. Capabilities, training, models, context, and prompting all matter in terms of the results that you might or might not get.
UPDATE:
OK, OK… I had to try that in Cursor, too, allowing Cursor to auto-select the model to use and I got similar results for the same prompt:
Checking if this codebase already uses OpenAPI, then summarizing best practices for defining OpenAPI specs in Elixir.
[5 tools called]
Checking dependencies and project structure:
[3 tools called]
Checking for a web framework and summarizing best practices for OpenAPI in Elixir:
[2 tools called]
Here are the best ways to define OpenAPI specs in Elixir:
Recommended approach: OpenApiSpex (code-first)
OpenApiSpex is the most popular library for OpenAPI in Elixir. It uses a code-first approach where you define schemas and operations alongside your code.
Key features
- Code-first: define schemas as Elixir modules using
OpenApiSpex.schema/1
- Request/response validation: automatic validation of params and body
- Test helpers: validate responses against the spec in tests
- Swagger UI integration: serve interactive documentation via plugs
- Active maintenance: actively maintained, supports Elixir ~> 1.14
Basic example structure
# Define schemas as modules
defmodule MyApp.Schemas.User do
require OpenApiSpex
alias OpenApiSpex.Schema
OpenApiSpex.schema(%{
title: "User",
type: :object,
properties: %{
id: %Schema{type: :integer, description: "User ID"},
name: %Schema{type: :string, description: "User name"},
email: %Schema{type: :string, format: :email}
},
required: [:id, :name, :email],
example: %{
id: 1,
name: "John Doe",
email: "john@example.com"
}
})
end
# Define operations in your controller
defmodule MyAppWeb.UserController do
use MyAppWeb, :controller
use OpenApiSpex.ControllerSpecs
alias MyApp.Schemas.User
@doc """
Get user by ID
"""
@spec show(Plug.Conn.t(), map()) :: Plug.Conn.t()
def show(conn, %{"id" => id}) do
# Your implementation
end
def action(conn, _) do
apply(__MODULE__, action_name(conn), [conn, conn.params])
end
end
Alternative: Oaskit (OpenAPI 3.1)
Oaskit is newer and supports OpenAPI 3.1 with JSON Schema validation. It’s good if you need 3.1 features, but some features are still in development.
Best practices
- Code-first: define schemas as modules near your controllers
- Validate inputs and outputs: use test helpers to ensure responses match the spec
- Include examples: add example values to schemas
- Modular schemas: reuse schema modules across operations
- CI/CD integration: validate specs in your build pipeline
- Serve UI: use SwaggerUI or Redoc plugs for interactive docs
For your project
Since this appears to be a system component architecture, consider:
- Adding OpenApiSpex to components that expose HTTP APIs
- Defining schemas in a shared location if multiple components need them
- Generating static specs for production and dynamic specs for development
Should I help set up OpenApiSpex for a specific component, or show how to structure schemas for your architecture?