Generate API documentation from RSpec examples with DOX

Melita Kokot
  —  7 min read

When developing an API, one should also write documentation so other developers know how to integrate with it. The problem is, nobody likes to write it. It’s an uninteresting task that takes additional time and feels like not doing “real” productive work.

Manual documentation maintenance can be time-consuming and error-prone. Due to human error, things are often overlooked, forgotten or accidentally replaced. Most developers were involved in unnecessary debugging sessions because API was changed but the documentation was not properly updated.

What if I told you there's a way to build documentation and keep it up-to-date with basically no effort?

Automate it

As responsible developers we are, besides writing unit tests, we also do integration tests. We could use our tests to extract real request/response examples and generate the documentation by executing the tests.

We can easily get request info (path, http verb, query params, body, headers) and response info (status, body, headers) from our controller and request tests.

Since our tests already have all the data we need for writing documentation, we could have a better, faster and a more accurate approach on building API documentation.

In the light of this revelation, editing documentation manually makes no sense whatsoever.

Presenting Dox

Automate your API documentation

We've built a gem for generating documentation from RSpec tests for Rails and we called it Dox. It uses the request/response information from your test examples and you only need to write some metadata using the Dox DSL. It generates API Blueprint formatted markdown.

To see it in action, check out our demo app.

Basic usage

It's simple and easy to get started documenting your API. Let's walk through the 4 steps to document a resource:

1. Define a resource descriptor using Dox DSL:

module Docs
  module Pokemons
    extend Dox::DSL::Syntax

    document :api do # generates module Docs::Pokemons::Api
      resource 'Pokemons' do
        endpoint '/pokemons'
        group 'Pokemons'
      end
    end

    document :show do # generates module Docs::Pokemons::Show
      action 'Get a pokemon'
    end
  end
end

You’ll need a descriptor for each resource. We usually put them in "spec/descriptors".

2. Include it in the test file:

  • include a resource module at the top of the resource test
  • include an action module in the action
  • tag the examples you want to document with the meta tag :dox .
RSpec.describe 'Pokemons', type: :request do
  include Documentation::Pokemons::Api

  let(:pikachu) { create(:pokemon) }

  describe 'GET /pokemons/:id' do
    include Documentation::Pokemons::Show

    it 'gets a pokemon', :dox do
      get pokemon_path(pikachu)
      expect(response).to have_http_status(200)
    end
  end
end

3. Run the tests you want to document with these tags:

$ bundle exec rspec spec/requests/v1 -f Dox::Formatter --order defined --tag dox --out docs.md

This will generate a markdown file in the API Blueprint format (we'll get to this a bit later).

4. Render the markdown to HTML.

Use one of the renderers like Aglio or Snowboard to convert generated markdown to a nicely-styled HTML. You can also use Apiary.io and let it generate an HTML for you and host the documentation as well.

Here's an example with Aglio:

$ aglio -i docs.md -o docs.html

Pokemons

That's it - now you can change your API responses and documentation can be updated with just two shell commands.

To check out all options and configuration details, please check out the Readme.

Behind the scenes

Dox provides an RSpec output formatter, called Dox::Formatter which inherits RSpec::Core::Formatter. Formatter defines the output one can see in the console when running the tests. Dox::Formatter spits out the output in the API Blueprint format.

When the tests are run with the -f Dox::Formatter flag, included modules actually append meta tags to each example needed for Dox::Formatter.

RSpec.describe 'Pokemons', type: :request, resource_name: 'Pokemons',
  resource_group: 'Pokemons', resource_endpoint: '/pokemons' do

  let(:pikachu) { create(:pokemon) }

  describe 'GET /pokemons/:id', action_name: 'Get a pokemon' do

    it 'gets a pokemon', :dox do
      get pokemon_path(pikachu)
      expect(response).to have_http_status(200)
    end
  end
end

Dox extracts the following info from the request object of a test example (ActionDispatch::Request):

  • request path
  • http verb
  • query params
  • body
  • headers

and from the response object (ActionDispatch::Response):

  • status
  • body
  • headers

Then Dox::Formatter generates the API Blueprint output. API Blueprint is a powerful high-level API description language for web APIs and it's open source. You can learn more about it here.

API Blueprint snippet for the example above looks like this:

# Group Pokemons

## Pokemons [/pokemons]

### Get a pokemon [GET /pokemons/{id}]

+ Parameters
    + id: `1` (number, required)


+ Request returns a pokemon
**GET**  `/api/v1/authors/1`

    + Headers

            Accept: application/json
            Content-Type: application/json

+ Response 200

    + Headers

            Content-Type: application/json; charset=utf-8

    + Body

            {
              "id": 1,
              "name": "Pikachu",
              "pokemon_type": "electric",
              "created_at": "2016-10-24T19:24:20.158Z",
              "updated_at": "2016-10-24T19:24:20.158Z"
            }

All that's left is to render the markdown to an HTML to get a styled and nice looking documentation file.

Rendering options

There are a few options when it comes to rendering the HTML.

Aglio

Aglio is currently most popular renderer.

It comes with a few predefined themes and layouts with support for generating a custom color theme or a Jade template.

Checkout out our demo app API documentation rendered with Aglio.

Snowboard

Snowboard is a new and fast API Blueprint parser and renderer written in Go.

You can write your own custom template. The default template is using Semantic UI. It can host the documentation and auto-regenerate it as you change the blueprint; it can validate the API blueprint file and it also supports mocking the server.

Apiary

Apiary is a service for API specification and prototyping. It can be used as a documentation hosting service. It has very nice looking theme, check out the Dox demo. To use it with Dox, install apiary-cli gem and simply push the markdown to your project on Apiary.

Include it in your CI process

To keep the documentation always up to date, it's best to integrate generating the documentation and publishing it to your CI setup. Simply generate the documentation and push it to your hosting service of choice (Apiary, S3, custom server, ...)

We usually add a few rake tasks for previewing and publishing the documentation.

namespace :api do
  namespace :doc do
    desc 'Generate API documentation markdown'
    task :md do
      require 'rspec/core/rake_task'

      RSpec::Core::RakeTask.new(:api_spec) do |t|
        t.pattern = 'spec/controllers/api/v1/'
        t.rspec_opts = "-f Dox::Formatter --order defined --tag dox --out public/api/docs/v1/apispec.md"
      end

      Rake::Task['api_spec'].invoke
    end

    task html: :md do
      `aglio -i public/api/docs/v1/apispec.md -o public/api/docs/v1/index.html`
    end

    task open: :html do
      `open public/api/docs/v1/index.html`
    end

    task publish: :md do
      `apiary publish --path=public/api/docs/v1/apispec.md --api-name=doxdemo`
    end
  end
end

With these tasks, you'll just need to setup the test database (if you're using it in the tests) and run publish task:

RAILS_ENV=test bundle exec rake db:setup
bundle exec rake api:doc:publish

Conclusion

Using Dox made documentation maintenance less painful and time consuming. Our mobile and frontend engineers are much happier because our documentation is always up to date with the CI process.

Dox is simple to use and it extracts enough data from the tests to give you a minimal documentation, yet it provides options to override some attributes and add custom markdown descriptions where needed. It should take you no time to plug it in your Rails/RSpec API app and start enjoying the extra time.

How do you document your APIs?

48346133-B7BD-45CD-9E24-6CC0D844E9FF
Greetings from our lovely team!
1/4
Achievement unlocked
Resize Master