Rolodex v0.8.0 Rolodex

Rolodex generates documentation for your Phoenix API.

Rolodex inspects a Phoenix Router and transforms the @doc annotations on your controller actions into documentation in the format of your choosing.

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

  1. Traverse your Phoenix 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 - See Rolodex.Schema for details on how define reusable parameter schemas. See Rolodex.RequestBody for details on how to use schemas in your API request body definitions. See Rolodex.Response for details on how to use schemas in your API response definitions. See Rolodex.Headers for details on how to define reusable headers for your route doc annotations and your responses.
  • Structured annotations - See Rolodex.Route for details on how to format annotations on your API route action functions for the Rolodex parser to handle
  • Generic serialization - The Rolodex.Processor behaviour encapsulates the basic steps needed to serialize API metadata into documentation. Rolodex ships with a valid OpenAPI (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)

High level example

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

  scope "/api" do
    pipe_through [:api]

    get "/test", MyController, :index
  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",
      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