Tutorial

Create Swagger compatible custom Phoenix JSON generator

This post was updated 01 May - 2020

api swagger phoenix

Swagger is one of the most popular API documentation tools and is open source. And they describe themselves as:

"Swagger UI allows anyone - be it your development team or your end consumers - to visualize and interact with the API’s resources without having any of the implementation logic in place."

https://swagger.io/tools/swagger-ui/

The goal is to be able to type mix mix swagger.gen.json .. instead of mix phx.gen.json ..

STEP 1

A good place to start is to copy the actual generator from the files that was created when the project was generated.

mkdir -p lib/mix/tasks
cp deps/phoenix/lib/mix/tasks/phx.gen.json.ex lib/mix/tasks/swagger.gen.json.ex

Then open the file and rename the module to

Mix.Tasks.Swagger.Gen.Jason

And in the file, we also need to change all instances of phx to swagger

The commands to create the folder and copy the generator files over are:

mkdir -p priv/templates/swagger.gen.json
cp -a deps/phoenix/priv/templates/phx.gen.json/. priv/templates/swagger.gen.json

If you managed to to this without mistakes you should be able to run:

mix compile && mix help | grep 'swagger'

If everything works, you should see something like:

STEP 2

For this to work, you also need to have the Phoenix Swagger library installed. So, add that to the mix.exs

{:phoenix_swagger, "~> 0.8"},
{:ex_json_schema, "~> 0.5"}

And run:

mix deps.get

In the config file config/config.exs add the package configuration:

config :tutorial, :phoenix_swagger,
  swagger_files: %{
    "priv/static/swagger.json" => [
      router: TutorialWeb.Router,     # phoenix routes will be converted to swagger paths
      endpoint: TutorialWeb.Endpoint  # (optional) endpoint config used to set host, port and https schemes.
    ]
  }

config :phoenix_swagger, json_library: Jason # If you dont use Poison

You also need to add a route to Swagger UI and add a Swagger definition in the router. Open lib/tutorial_web/router.ex and add:

scope "/api/swagger" do
    forward "/", PhoenixSwagger.Plug.SwaggerUI, otp_app: :tutorial, swagger_file: "swagger.json"
  end

  def swagger_info do
    %{
      info: %{
        version: "1.0",
        title: "Tutorial App - Fullstack Phoenix"
      }
    }
  end

Swagger works by reading a swagger.json file where all the API-information is documented. That file is auto generated with a generator that comes with the package.

Now you should be able to test this by running:

mix phx.swagger.generate

And see something like:

STEP 3

All that is left to change is to modify the controller template. priv/templates/swagger.gen.json/controller.ex

I wont post all code in here but I will link to the changes in Github.

But there are basically 3 types of changes.

  1. Add the use PhoenixSwagger in the top of the file
  2. Add a swagger_path :index do for every type of action
  3. In the bottom, add a def swagger_definitions do function.

When is run, we will end up with code that looks like this for the index function:

swagger_path :index do
    get "/api/users"
    summary "List users"
    description "List all users in the database"
    tag "Users"
    produces "application/json"
    response(200, "OK", Schema.ref(:UsersResponse),
      example: %{
        data: [
          %{
            id: 1,
            count: 42,
            email: "some email",
            name: "some name",
            private: true,
            profile: "some profile",
          }
        ]
      }
    )
  end

  def index(conn, _params) do
    users = Users.list_users()
    render(conn, "index.json", users: users)
  end

And similar code for all other generated functions.

STEP 4

Next step is to test this out. I want to generate an API endpoint for users so I run:

mix swagger.gen.json Users User users name email profile:text private:boolean count:integer

I should get the normal output and the message that I need to add the users routes.

scope "/api", TutorialWeb do
    pipe_through :api
    resources "/users", UserController, except: [:new, :edit]
  end

Then I can run:

mix ecto.migrate

And after that I can rerun the command to update my swagger documentation. I need to do this since I have generated to controller that tells swagger about my new endpoints.

mix phx.swagger.generate

Start the server and navigate to http://localhost:4000/api/swagger

You should now see:

And if you click on one of the endpoint, there is more documentation:

Related Tutorials

Published 04 May - 2021
Updated 05 May - 2022

How to combine Phoenix LiveView with Alpine.js

No matter how great Phoenix LiveView is, there is still some use case for sprinking some JS in your app to improve UX. For example, tabs, dropdowns,..

Published 30 Jul - 2020
Updated 04 Feb - 2022

Getting started with GraphQL and Absinthe in Phoenix

In the last tutorial, there I had an app with a simple rest api that was authenticated with Guardian and Json Web Token. In this tutorial, I will go..