OpenApiSpex - OpenAPI / Swagger 3.0 for Plug APIs

Leverage Open Api 3.0 (Swagger) to document, test, validate and explore your Plug and Phoenix APIs.

  • Generate and serve a JSON Open API (swagger) document from your code
  • Use the spec to cast request params to well defined schema structs
  • Validate params against schemas, eliminate bad requests before they hit your controllers
  • Validate responses against schemas in tests, ensuring your docs are accurate and reliable
  • Explore the API interactively with with SwaggerUI

Github: https://github.com/mbuhot/open_api_spex
Docs: https://hexdocs.pm/open_api_spex/
Hex: https://hex.pm/packages/open_api_spex

Integrating with phoenix controllers:

  @spec open_api_operation(atom) :: Operation.t
  def open_api_operation(action), do: apply(__MODULE__, :"#{action}_operation", [])

  @spec show_operation() :: Operation.t
  def show_operation() do
    %Operation{
      tags: ["users"],
      summary: "Show user",
      description: "Show a user by ID",
      operationId: "UserController.show",
      parameters: [
        Operation.parameter(:id, :path, :integer, "User ID", example: 123)
      ],
      responses: %{
        200 => Operation.response("User", "application/json", UserResponse)
      }
    }
  end
  def show(conn, %{"id" => id}) do
    {:ok, user} = MyApp.Users.find_by_id(id)
    json(conn, 200, user)
  end

Cast and validate params:

  plug OpenApiSpex.Plug.Cast
  plug OpenApiSpex.Plug.Validate

  def open_api_operation(action) do
    apply(__MODULE__, :"#{action}_operation", [])
  end

  def create_operation do
    import Operation
    %Operation{
      tags: ["users"],
      summary: "Create user",
      description: "Create a user",
      operationId: "UserController.create",
      parameters: [],
      requestBody: request_body("The user attributes", "application/json", UserRequest),
      responses: %{
        201 => response("User", "application/json", UserResponse)
      }
    }
  end

  def create(conn, request = %UserRequest{user: %User{name: name, email: email, birthday: birthday = %Date{}}}) do
    ...  
end
6 Likes

Version 2.3.0 released.
Now supports validating enum string types.

2 Likes

Hi,

Is there way to keep specs in a separate files?

@lessless can you elaborate on what you’re looking for?

OpenApiSpex doesn’t support reading external JSON/YAML files, but there is some flexibility in organizing the Elixir code.

Plug application example clarified it. I thought about extracting specs in separate .ex files and importing them later on, but having a module per http action is even better.

1 Like

Version 3.2.0 released.

Lots of internal improvements and some new features thanks to the wonderful contributors, particularly @moxley and the team at Ghost Group / Weedmaps. :heart:

Highlights:

  • Jason library support for JSON serialization
  • Improved memory usage
  • Improved error messages and validations
  • Integrate SwaggerUI with Plug.CSRFProtection

See the changelog for more details.

6 Likes

Version 3.4.0 released.

This version is required when using Phoenix 1.4.7+ and the OpenApiSpex.Paths.from_router/1 function.

Highlights:

  • Open API extensions can be added to the OpenApi document and the Info structs
  • Server.from_endpoint now uses the Phoenix API instead of the endpoint config to determine the URL
  • Compatibility with Phoenix 1.4.7 Router modules
3 Likes

amazing lib! thanks @mbuhot!
I loved the idea that the controller tests are guaranteeing the API protocol, so when our mobile/web devs (or us) access the swaggerui, they will see a guaranteed documentation

1 Like

In the example the api data is returned by show_operation which is just a function, so you can pull the %Operation{} data from anywhere.

1 Like