Ruby
Article

Do the Right Thing and Document Your Rails API with Swagger

By Parth Modi

You probably spend most of your programming time trying to make your code better or faster or more efficient, don’t you? How much time to you spend actually documenting your code, though? Not a lot, if you’re like most of us, but documentation isn’t that important, right?

Maybe, if you are working on a small project and your team consists of just one member and that’s you. But even then you won’t be there to maintain that little project of yours forever. If someday, someone walks in and tries to understand how it all works in order to implement another feature or fix a bug, it might take much much longer to just get started. Besides, we all know that lost feeling when we encounter a large, undocumented code base and try to fix a bug. “Code as documentation” doesn’t always (or ever?) work.

For APIs, this is far more important as the system you are developing is going to be used by others through endpoints you expose. Consider a scenario when you have to design a back-end solution for a business problem, which will be used by a front-end or mobile teams. It would be inconvenient to share Postman collections every time an API is changed or implemented. This will slow down the development pace and cause unnecessary friction. Thankfully, we have lots of tools available to implement documentation without having to write a lot of code and example responses.

Today I will walk you through the process of creating APIs with documentation via Swagger. Swagger has two parts: swagger-docs which generates docs for your API, and swagger-ui which uses JSON generated by swagger-docs to build a UI used to consume and interact with those APIs.

Our JSON API

We will use Rails start the project by using the --api option so that we can start with the minimum structure required for APIs:

rails new swagger-demo --api

Create a user model with name, age and status fields:

rails generate model User name age:integer status:integer

The status field will be defined as an enum:

# models/user.rb
model User < ApplicationRecord
  enum status: [:active, :inactive]
end

Create a users controller with basic CRUD actions using the scaffold_controller generator:

rails generate scaffold_controller api/v1/users

OK, our basic structure with CRUD actions is ready. Let’s add some documentation:

Setup Documentation with swagger-docs

To generate documentation we will use the swagger–docs gem. This gem will help us create JSON files which can be served to swagger-ui.

Installation

In the Gemfile:

gem 'swagger-docs'

Then,

bundle install

Configuration

Next, add an initializer called swagger_docs.rb in the config/initializers directory. Here is a basic configuration to get you started:

# config/initializers/swagger-docs.rb
Swagger::Docs::Config.register_apis({
  "1.0" => {
    # location where our api doc files will be generated, as of now we will store files under public directory
    :api_file_path => "public/",
    # base path url of our application
    # while using production mode, point it to production url
    :base_path => "http://localhost:3000",
    # setting this option true tells swagger to clean all files generated in api_file_path directory before any files are generated
    :clean_directory => true,
    # As we are using Rails-API, our ApplicationController inherits ActionController::API instead of ActionController::Base
    # Hence, we need to add ActionController::API instead of default ActionController::Base
    :base_api_controller => ActionController::API,
    # parent_controller needs to be specified if API controllers are inheriting some other controller than ApplicationController
    # :parent_controller => ApplicationController,
    :attributes => {
      :info => {
        "title" => "Swagger Demo",
        "description" => "How Swagger works",
        "contact" => "parthmodi54@yahoo.com",
        "license" => "Apache 2.0",
        "licenseUrl" => "http://www.apache.org/licenses/LICENSE-2.0.html"
      }
    }
  }
})

For more information on options available, see the swagger-docs Configuration options portion of the documentation.

Documenting the UsersController APIs

swagger-docs provides it’s own DSL for documenting APIs. The Following line registers UsersController with swagger-docs:

swagger_controller :users, "User Management"

Next, we need to add documentation for each action in the controller. We can specify required and optional parameters, types of parameters (such as string, integer, enum), and how we want those parameters (in form-data, body, or multipart form-data, etc.)

Look at the example code shown below for the create action of UsersController:

class Api::V1::UsersController < ApplicationController
  .....
  # POST /users
  swagger_api :create do
    summary "To create user"
    notes "Implementation notes, such as required params, example queries for apis are written here."
    param :form, "user[name]", :string, :required, "Name of user"
    param :form, "user[age]", :integer, :optional, "Age of user"
    param_list :form, "user[status]", :string, :required, "Status of user, can be active or inactive"
    response :success
    response :unprocessable_entity
    response :500, "Internal Error"
  end
  def create
    @user = User.new(user_params)
    if @user.save
      render json: @user, status: :created
    else
      render json: @user.errors, status: :unprocessable_entity
    end
  end
  .....
end

swagger_api is used to specify the API name and a description. param specifies a parameter name, type, and description. We use parameters that are nested in the user hash, hence we use user[name].

response lists which response codes and types that the API generates. It’s good practice to mention all possible response codes that the API may produce.

Other CRUD actions are documented in a similar manner. This might be enough for basic CRUD, but to dive deeper and know about some interesting things like DRYing up documentation, head over to the swagger-docs docs.

Generating and Viewing Documentation

The following Rake task will generate the docs for the controllers/actions that have been configured:

rake swagger:docs

This will generate documentation files at the path we specified in the API_FILE_PATH option of the initializer. If everything goes smoothly, you will see a few files generated in the public/ directory, specifically inside the public/api/v1/. If the files are not generated, run the following command to see a more verbose console output and debug what’s happening:

SD_LOG_LEVEL=1 rake swagger:docs

Note: If you are getting the error undefined method "swagger_controller" for API::V1::UsersController::Class, then you need to set the base_api_controller option explicitly as shown below:

# config/initializers/swagger-docs.rb
Swagger::Docs::Config.base_api_controller = ActionController::API

This is explained in detail here.

Great! Now we have the documentation ready. Go to the browser and type localhost:3000/api-docs.json to check out the generated JSON files. Now we can setup swagger-ui to use these docs.

Setting Up Swagger-UI

swagger-ui is a standalone structure built in HTML, CSS and, JS which provides a beautiful user interface that can be used to interact and play around with our APIs. The good part is the UI is generated dynamically with the JSON files we provide.

Installation

Follow this procedure to install swagger-uia:

  • Go to the swagger-ui repo and clone it or download a release.

  • Place the contents of the dist directory along with the index.html inside the path you specifed in the api_file_path option. Based on our configuration, it is the public/ directory. This is where you will find api-docs.json. If you have changed api_file_path to some other location, then you will need to place files accordingly. Just keep in mind to keep both index.html and api-docs.json inside the same directory.

  • In index.html, change the url to point to the api-docs.json file:

// get the url from search bar
var url = window.location.search.match(/url=([^&]+)/);
if (url && url.length > 1) {
  url = decodeURIComponent(url[1]);
} else {
  // point to api-docs.json to read API documentation
  url = "/api-docs.json";
}

All set! Type in http://localhost:3000/index.html and see the code in action.

swagger_overview

Clicking on the Try it out button will run the API action and show the result:

Here is the link to example project repository swagger-demo with all actions documented if you get stuck.

Conclusion

We implemented swagger-docs to document our APIs, using swagger-ui to build the UI dynamically from our documentation. After this initial setup, you just need to run rake swagger:docs every time you change or implement an API and swagger-ui will generate this UI for you.

Documenting your API is this manner is the right thing to do. It allows consumers to quickly understand what your API offers, allowing them to test out actions and get to writing their front end code with minimal conflict.

  • Kaio Cristian Costa Porto de M

    The idea of mixing code logic with documentation logic doesn’t make sense for me. I would rather use the web version of swagger than add more code to my controllers.

    • ggsp

      That’s a fair point, but you then have 2 places to make updates. It’s a trade-off.

      • Kaio Cristian Costa Porto de M

        Agreed. My main concern with it is mostly about mixing logics, I would rather have 2 places to change but 2 clean and logic specific places than having 1 mixed. But is is mostly about preferences.

  • AMC

    I’m really happy Swagger is getting some press in the Ruby community! I’m a little sad, though, that this article didn’t discuss the tools available for programmatically validating your API. I specifically work with Apivore, which makes it a breeze to ensure our documentation is accurate (a surprising amount wasn’t before we started testing!). To me, the major value in using a standardized solution like Swagger is the community and OSS libraries around it, and in particular the awesomeness that is testable documentation.

    • https://about.me/jeff_dickey Jeff Dickey

      I’ve also been disappointed that all the decent Swagger tooling for Ruby that I’ve seen is tied to Rails (or Grape, who rolled their own). If you’re working with Sinatra, Roda, or some such, you’re back to swagger-editor directly editing a YAML file.

      Unless someone has found something I’ve missed…

      • AMC

        That’s a great point @jdickey:disqus! Apivore is doubly guilty because it’s also specific to RSpec rather than Minitest. Not a big deal in that, worst comes to worst, you can write just those tests in RSpec, but it probably wouldn’t be an unreasonable amount of work to make it testing framework agnostic. There are a LOT of RSpec-or-Minitest libraries out there.

        The dependency on the web framework, though, is much more understandable, since it hooks into the API the framework exposes for how to test web requests. It’s more difficult, though not impossible, to make that framework-agnostic.

        Bottom line, though, these tools are built by various shops for their use case, and the community using Swagger for Ruby apps just isn’t large enough yet to contribute at the level we’d need to see these projects be applicable beyond what those shops need. It’s not really reasonable to expect the maintainers to develop around all cases without contributions by people using other solutions.

        My theory is, the better we make these tools for their specific use cases (in this case Rails), the more the community grows, and eventually it’ll hit the critical mass that lets it branch out into framework-agnostic territory.

        One thing to note: I’m not particularly concerned about the lack of Swagger-doc-building tooling, since anyway I haven’t found the Rails tooling to be that amazing. It’s usually just a really thin, leaky abstraction layer, and I’ve seen another team in my company be more productive by doing the swagger-editor YAML thing than including any Railsy solutions.

        OTOH, Swagger validation is incredibly useful, and that’s probably the biggest win (though also the most difficult piece) to bring to non-Rails frameworks.

  • angelkar

    I also use Swagger-Blocks and it’s a more convenient solution compared to the rest of the gems out there. It’s still quite verbose to write API docs but that’s a more general problem and not tied to the gem itself. Writing docs is a task developers find boring and it’s the lowest of priorities :(

Recommended
Sponsors
Get the latest in Ruby, once a week, for free.