In this blog post we are going to demonstrate the usage of Swagger UI for Ruby on Rails , which is one of the best combinations for API documentation.
Ruby, with its reliable open source framework Rails, is an excellent option for building API’s. Especially, it is highly recommended for starting larger API projects. And with the Rails 5, it is mostly a breeze.
Rails 5 has some cool features, like generating and scaffolding an API without the hassles of the browser and html, with its integrated Rails-API gem. This also leads to a faster API-only application plus an easier rendering of data to json or XML.
Well, once the Rails API has been developed, the next is to document it. Enter Swagger UI… which documents an API both in machine and human readable form. While there are several products for documentation, we will stick on to Swagger with this post. It is truly more popular than many other frameworks for APIs, it has great support resources and is relatively easier. The Swagger UI is used to create client libraries to an API. This is done by building an API (in our case, with Rails), documenting the resource controllers of the API, and then generating a json Swagger Definition for the same. This will then be plugged into the Swagger UI which will create awesome documentation for the API. That’s it, to put it in a nutshell. And oh, of course, this is all done with the help of the Swagger-docs gem.
If that is too vague, let us break it down into steps with a generic example.
Step 1 : Install Swagger-docs
First and foremost, we need the swagger-docs gem installed. For this, we will use a sample application, and in the GemFile of the application, we include as follows:
gem‘swagger-docs’
Save the file and run ‘bundle’ or we can install it by:
$ geminstall ‘swagger-docs’
Now, we have the swagger-docs gem installed in our system.
Step 2 : Create a Initializer
Next, we have to create an initializer in config/initializer and define our API. An example of config/initializer is swagger_docs.rb.
Swagger::Docs::Config.register_apis({ "1.0" => { # the extension used for the API :api_extension_type => :json, # the output location where your .json files are written to :api_file_path => "public/api/v1/", # the URL base path to your API :base_path => "http://api.somedomain.com", # if you want to delete all .json files at each generation :clean_directory => false, # Ability to setup base controller for each api version. Api::V1::SomeController for example. :parent_controller =>Api::V1::SomeController, # add custom attributes to api-docs :attributes => { :info => { "title" => "Swagger Sample App", "description" => "This is a sample description.", "termsOfServiceUrl" => "http://agiratech.com/", "contact" => "contact@agiratech.com", } } } })
This is the minimal configuration settings. Following is the table that lists the entire configuration attributes and their default values:
Configuration options
The following table shows all the current configuration options and their defaults. The default will be used if you don’t supply your own value.
Option | Description | Default |
api_extension_type | The extension, if necessary, used for your API – e.g. :json or :xml | nil |
api_file_path | The output file path where generated swagger-docs files are written to. | public/ |
base_path | The URI base path for your API – e.g. api.somedomain.com | / |
base_api_controller / base_api_controllers | The base controller class your project uses; it or its subclasses will be where you call swagger_controller and swagger_api. An array of base controller classes may be provided. | ActionController::Base |
clean_directory | When generating swagger-docs files this option specifies if the api_file_path should be cleaned first. This means that all files will be deleted in the output directory first before any files are generated. | false |
formatting | Specifies which formatting method to apply to the JSON that is written. Available options: :none, :pretty | :pretty |
camelize_model_properties | Camelizes property names of models. For example, a property name called first_name would be converted to firstName. | true |
parent_controller | Assign a different controller to use for the configuration |
Step 3 : Document the API Controllers
After setting the initializer, we will next document the API controllers. Let us use a sample API controller, say a BooksController.
#File app/controllers/api/v0/registrations_controller.rb classApi::V1::BooksController<BooksController #Add this line swagger_controller :books, "Books Management" #Add this swagger_api block swagger_api :create do summary "Creates a new Book" param :form, :title, :string, "Title" param :form, :author, :string, "Author" param :form, :isbn, :string, :required, "ISBN" param :form, :dop, :string, :required, "Date of Publication" response :unauthorized response :not_acceptable end swagger_api :show do summary "Fetches a single Book" param :path, :id, :integer, :optional, "Book Id" response :ok, "Success", :Book response :unauthorized response :not_acceptable response :not_found end . . . end
Following is the table that describes the methods of the DSL of swagger_api block:Similarly, we document all controllers and methods. Suppose there is a common
header or parameter that is to be used across controllers and methods, we could use set them in the API BaseController and have them inherited. This makes the base controller as a superclass to all other controllers and the subclasses will be documented accordingly. In fact, there are two ways to do this… one as stated above, and the other way is to use a block for the swagger_api definition using self.add_common_params(api). But we shall talk about this in another post. For now, we will stick with the procedure mentioned so far.
<tbody “>
Method | Description |
summary | The summary of the API |
notes (optional) | The associated notes for the API |
param | Standard API Parameter |
param_list | Standard API Enum/List parameter. |
response | Takes a symbol or status code and passes it to `Rack::Utils.status_code`. The current list of status codes can be seen here: https://github.com/rack/rack/blob/master/lib/rack/utils.rb. An optional message can be added. |
Step 4: Generate json file
Run Rake command and generate json files with the following command:
rakeswagger:docs
from the Rails root directory. Yes, it is simple. This command goes through all controllers and determines those that have to be documented. Since we have already created the configuration of all the API methods, the Swagger UI json files would be generated and available in your api_file_path (e.g. ./public/api/v1). This is the path that we have set in the config/initializers/swagger_docs.rb file.
But what if a generation fails?
Errors will not be displayed by default. We will need to add an additional attribute to the rake task in order to view all error messages generated during the execution. Therefore, we will run the rake command as follows:
SD_LOG_LEVEL=1 rake swagger:docs
Yes, by adding the environment variable SD_LOG_LEVEL, we will be able to log the errors but currently, only constantize errors are shown. These are written into $stderr, and the error logging methods which are listed in the config can be overridden for custom behaviour.
A sample JSON Output file will be as such:
{ "path": "/books", "operations": [ { "summary": "Creates a new Book", "parameters": [ { "paramType": "form", "name": "title", "type": "string", "description": "Title", "required": true }, { "paramType": "form", "name": "author", "type": "string", "description": "Author", "required": true }, { "paramType": "form", "name": "isbn", "type": "string", "description": "ISBN", "required": true { "paramType": "form", "name": "dop", "type": "string", "description": "Date of Publication", "required": true } "code": 406, "message": "Not Acceptable" } ], "method": "post", "nickname": "Api::V1::Books#create" } ] }, { "path": "/books/{id}", "operations": [ { "summary": "Fetches a single Book item", "parameters": [ { "paramType": "path", "name": "id", "type": "string", "description": "Book ISBN", "required": false } ], "responseMessages": [{ "code": 401, "message": "Unauthorized" }, { "code": 404, "message": "Not Found" }, { "code": 406, "message": "Not Acceptable" } ], "method": "get", "nickname": "Api::V1::Book#show" } ] } }
Step 5: Integrate the Swagger UI
Finally, we shall integrate the Swagger UI index view with the Rails application public directory. This is a good practice for better readability of the project. To do this is another simple procedure.
Copy all Swagger UI related files from the GitHub and place the directory in the rails project “public/api/api” directory. Place the index.html file to “public/api”. This means all the swagger ui resource files are under “public/api/api” while the swggerui home page is available under “public/api”.
Finally, we are ready to view the Swagger UI documentation for the sample Rails application. Restart the Rails server if it is running, and then go to the index file of the application in the browser.
There are several other advanced customizations possible, which we can see in another post. Some of the common customizations are to inherit from a custom API controller, overriding the routes that swagger-docs traverses to find controllers, overriding the default swagger-docs behaviour, etc.
Also, in this post, we are dealing with v.1.2 swagger specifications. With the v2.0, we will use Swagger-Blocks.
Check out the links in this post for chain information and procedures. Happy coding and documentation, guys. For more on Ruby on Rails follow Agira Technologies.