Rolodex v0.10.1 Rolodex

Rolodex generates documentation for your Phoenix API.

Rolodex transforms the structured @doc annotations on your Phoenix Controller action functions into documentation API documentation in the format of your choosing. Rolodex ships with default support for OpenAPI 3.0 (Swagger) docs.

Rolodex.run/1 encapsulates the full documentation generation process. When invoked, it will:

  1. Traverse your Rolodex Router
  2. Collect documentation data for the API endpoints exposed by your router
  3. Serialize the data into a format of your choosing (e.g. Swagger JSON)
  4. Write the serialized data out to a destination of your choosing.

Rolodex can be configured in the config/ files for your Phoenix project. See Rolodex.Config for more details on configuration options.

Features and resources

  • Reusable components — Support for reusable parameter schemas, request bodies, responses, and headers.
  • Structured annotations — Standardized format for annotating your Phoenix Controller action functions with documentation info
  • Generic serialization - The Rolodex.Processor behaviour encapsulates the basic steps needed to serialize API metadata into documentation. Rolodex ships with a valid OpenAPI 3.0 (Swagger) JSON processor (see: Rolodex.Processors.OpenAPI)
  • Generic writing - The Rolodex.Writer behaviour encapsulates the basic steps needed to write out formatted docs. Rolodex ships with a file writer ( see: Rolodex.Writers.FileWriter)

Further reading

High level example

# Your Phoenix router
defmodule MyPhoenixRouter do
  pipeline :api do
    plug MyPlug
  end

  scope "/api" do
    pipe_through [:api]

    get "/test", MyController, :index
  end
end

# Your Rolodex router, which tells Rolodex which routes to document
defmodule MyRouter do
  use Rolodex.Router

  router MyPhoenixRouter do
    get "/api/test"
  end
end

# Your controller
defmodule MyController do
  @doc [
    auth: :BearerAuth,
    headers: ["X-Request-ID": uuid, required: true],
    query_params: [include: :string],
    path_params: [user_id: :uuid],
    body: MyRequestBody,
    responses: %{200 => MyResponse},
    metadata: [public: true],
    tags: ["foo", "bar"]
  ]
  @doc "My index action"
  def index(conn, _), do: conn
end

# Your request body
defmodule MyRequestBody do
  use Rolodex.RequestBody

  request_body "MyRequestBody" do
    desc "A request body"

    content "application/json" do
      schema do
        field :id, :integer
        field :name, :string
      end

      example :request, %{id: "123", name: "Ada Lovelace"}
    end
  end
end

# Some shared headers for your response
defmodule RateLimitHeaders do
  use Rolodex.Headers

  headers "RateLimitHeaders" do
    header "X-Rate-Limited", :boolean, desc: "Have you been rate limited"
    header "X-Rate-Limit-Duration", :integer
  end
end

# Your response
defmodule MyResponse do
  use Rolodex.Response

  response "MyResponse" do
    desc "A response"
    headers RateLimitHeaders

    content "application/json" do
      schema MySchema
      example :response, %{id: "123", name: "Ada Lovelace"}
    end
  end
end

# Your schema
defmodule MySchema do
  use Rolodex.Schema

  schema "MySchema", desc: "A schema" do
    field :id, :uuid
    field :name, :string, desc: "The name"
  end
end

# Your Rolodex config
defmodule MyConfig do
  use Rolodex.Config

  def spec() do
    [
      title: "MyApp",
      description: "An example",
      version: "1.0.0"
    ]
  end

  def render_groups_spec() do
    [router: MyRouter]
  end

  def auth_spec() do
    [
      BearerAuth: [
        type: "http",
        scheme: "bearer"
      ]
    ]
  end

  def pipelines_spec() do
    [
      api: [
        headers: ["Include-Meta": :boolean]
      ]
    ]
  end
end

# In mix.exs
config :rolodex, module: MyConfig

# Then...
Application.get_all_env(:rolodex)[:module]
|> Rolodex.Config.new()
|> Rolodex.run()

# The JSON written out to file should look like
%{
  "openapi" => "3.0.0",
  "info" => %{
    "title" => "MyApp",
    "description" => "An example",
    "version" => "1.0.0"
  },
  "paths" => %{
    "/api/test" => %{
      "get" => %{
        "security" => [%{"BearerAuth" => []}],
        "metadata" => %{"public" => true},
        "parameters" => [
          %{
            "in" => "header",
            "name" => "X-Request-ID",
            "required" => true,
            "schema" => %{
              "type" => "string",
              "format" => "uuid"
            }
          },
          %{
            "in" => "path",
            "name" => "user_id",
            "schema" => %{
              "type" => "string",
              "format" => "uuid"
            }
          },
          %{
            "in" => "query",
            "name" => "include",
            "schema" => %{
              "type" => "string"
            }
          }
        ],
        "responses" => %{
          "200" => %{
            "$ref" => "#/components/responses/MyResponse"
          }
        },
        "requestBody" => %{
          "$ref" => "#/components/requestBodies/MyRequestBody"
        },
        "tags" => ["foo", "bar"]
      }
    }
  },
  "components" => %{
    "requestBodies" => %{
      "MyRequestBody" => %{
        "description" => "A request body",
        "content" => %{
          "application/json" => %{
            "schema" => %{
              "type" => "object",
              "properties" => %{
                "id" => %{"type" => "string", "format" => "uuid"},
                "name" => %{"type" => "string", "description" => "The name"}
              }
            },
            "examples" => %{
              "request" => %{"id" => "123", "name" => "Ada Lovelace"}
            }
          }
        }
      }
    },
    "responses" => %{
      "MyResponse" => %{
        "description" => "A response",
        "headers" => %{
          "X-Rate-Limited" => %{
            "description" => "Have you been rate limited",
            "schema" => %{
              "type" => "string"
            }
          },
          "X-Rate-Limit-Duration" => %{
            "schema" => %{
              "type" => "integer"
            }
          }
        },
        "content" => %{
          "application/json" => %{
            "schema" => %{
              "$ref" => "#/components/schemas/MySchema"
            },
            "examples" => %{
              "response" => %{"id" => "123", "name" => "Ada Lovelace"}
            }
          }
        }
      }
    },
    "schemas" => %{
      "MySchema" => %{
        "type" => "object",
        "description" => "A schema",
        "properties" => %{
          "id" => %{"type" => "string", "format" => "uuid"},
          "name" => %{"type" => "string", "description" => "The name"}
        }
      }
    },
    "securitySchemes" => %{
      "BearerAuth" => %{
        "type" => "http",
        "scheme" => "bearer"
      }
    }
  }
}

Link to this section Summary

Functions

Runs Rolodex and writes out documentation to the specified destination

Link to this section Functions

Link to this function

run(config)
run(Rolodex.Config.t()) :: :ok | {:error, any()}

Runs Rolodex and writes out documentation to the specified destination