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
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.
Here’s an example with Aglio:
$ aglio -i docs.md -o docs.html
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?