The “non-RESTful” version of Alexandria is ready. It’s now time to explain to our front-end developers how to use it! Writing documentation has became a fundamental part of creating web APIs.
To answer this need and simplify the process, many tools have been created to generate documentation. We are going to go over a few of them and pick one of them to write a bit of documentation for Alexandria.
First, let’s define the 10 rules that will help you create a great documentation for your APIs.
Your entire API should be documented. That means all the developers-facing endpoints should have clear instructions about their use.
Developers like to test before they implement something. Offering an easy way to copy/paste curl
requests is a good way of doing that. Other tools that we will talk about later on also come with such feature.
If developers are logged in, you can even include their API key directly in the documentation making it even easier for them to test your endpoints.
Developers should be capable of understanding the request flow based on your documentation. Theoretically, they should even be able to implement a client for your API without actually making any request.
The documentation for your API is going to be a great reference for all the features it has. Sadly, it’s easy to feel overwhelmed without a simple tutorial to get started.
A “Getting Started” guide should be available for your API to, at the very least, show how to get an API key, write the first request and get a response back.
In the first module, I mentioned that you’re building an API for others, not for yourself. Well, it’s now time to put yourself in someone else’s shoes, forget everything you know about the API and write down clear explanations for each resource.
The previous rule is only good to a certain extent. You can do your best to pretend that you don’t know anything about the API, but there’s going to be some stuff that you will miss. Even something you might think is common knowledge could cause a lot of frustration for implementers.
To validate your API, take a pool of developers and make them read the documentation. They should be able to understand how everything is structured using only the documentation, and without asking you any clarifying questions. Ask them to create a small program accessing one resource and do something with the result as a test.
Writing SDKs for your API will allow developers to jump right in using their favorite language. In other words,they won’t have to make HTTP calls because you will take care of that instead.
SDKs allow you to abstract the communication with your API, hiding the complexity and the HTTP calls so developers can simply use their favorite programming language to talk with your service.
Developers rely on your API and expect it to simply work. If something breaks, they need to know ASAP. That’s your responsibility to tell them when something goes wrong or when changes are coming.
You can let your community know what’s happening using a blog, a newsletter, or even just a changelog on GitHub.
If you open your API to a lot of people, you should also have a place for them to chat about it, ask for help, suggest features, give feedback and report bugs. This kind of platform will help you improve your API and make it better using your users’ ideas.
One day, your API will go down for some unknown reason. When it happens, people who rely on it need to know that there is something wrong on your side and that you’re working on fixing it.
A status page is the perfect way to accomplish both of these objectives.
If you don’t follow this rule, there is no point following the previous ones we just encountered. A documentation that is not up-to-date is pointless, and will only cause frustrations for your users.
Always ensure that new features or changes get documented. This can be made automatic or, just like automated tests, can be a requirement for any pull request. You wouldn’t create a pull request without tests, right? Well, not updating the documentation is essentially the same thing.
In this section, we are going to discuss a few tools that can be used to create documentation for web APIs.
The first option is to simply use a readme.md
file included in the project. If you’re familiar with GitHub, you know that this “readme” file will be displayed automatically when someone checks out your project. A “readme” can be written in different styles, but one of the simplest options today is to use Markdown.
Markdown is a text-to-HTML conversion tool for web writers.
With Markdown, you can write documentation for your API in any way you like with just words and a few nice formatting symbols. We could write documentation for the books resource like this for example:
#### GET /api/books
##### Authentication
- Header: Authorization
- Realm: Client Realm
- Example: `Authorization: Alexandria-Token api_key=API_KEY_ID:YOUR_API_KEY`
##### Fields
Representations can be built by specifying the wanted fields with the
`fields` query parameter. If no `fields` parameter is present, all available
fields will be returned.
Available Fields:
- `id`
- `title`
- `subtitle`
- `isbn_10`
- `isbn_13`
- `description`
- `released_on`,
- `publisher_id`
- `author_id`
- `created_at`
- `updated_at`
- `cover`,
- `price_cents`
- `price_currency`
Example:
```bash
curl -H "Authorization: Alexandria-Token api_key=ID:KEY" \
http://localhost:3000/api/books?fields=id,title,subtitle
```
##### Embeds
##### Sorting
##### Paginating
##### Filtering
Which would generate what you can see below.
Authorization:Alexandria-Token api_key=ID:KEY
Representations can be built by specifying the wanted fields with the
fields
query parameter. If no fields
parameter is present, all available
fields will be returned.
Available Fields:
id
title
subtitle
isbn_10
isbn_13
description
released_on
,
publisher_id
author_id
created_at
updated_at
cover
,
price_cents
price_currency
Example:
curl -H "Authorization: Alexandria-Token api_key=API_KEY_ID:YOUR_API_KEY" \
http://localhost:3000/api/books?fields=id,title,subtitle
For Alexandria, it would probably be better to include a general introduction addressing how the client can build representations for all the resources using field picking, embedding, sorting, pagination and filtering.
Overall, writing documentation like this is totally fine, but can be a bit “dull” for developers to read - especially if everything is in one file. Using the GitHub Wiki feature is a way to organize things in a better manner. However, there are alternatives that will generate good-looking documentation based on your markdown.
Writing documentation with Slate is pretty similar to what we just did since it also relies on Markdown. However, it also has a bunch of great features and beautiful generated views to show your documentation to the world.
Slate interface look like Figure 1.
If we wanted to write it for Alexandria, we might end up with something like Figure 2.
The markdown for this is available below:
---
title: API Reference
language_tabs:
- shell
- ruby
toc_footers:
- <a href='#'>Sign Up for a Developer Key</a>
- <a href='https://github.com/tripit/slate'>Documentation Powered by Slate</a>
includes:
- errors
search: true
---
# Introduction
Welcome to Alexandria API!
This example API documentation page was created with
[Slate](https://github.com/tripit/slate).
# Authentication
The Alexandria API has two different levels of authentication,
known as `Client Realm` and `User Realm`. In each resource description, the
required realm will be specified.
## Client Realm
> To authenticate, use this code:
```ruby
require 'alexandria'
api = Alexandria::APIClient.authenticate!('YOUR_API_KEY')
```
As you can see in the figures above, everything is organized and easy to navigate. The documentation generated by Slate is also responsive and can be viewed from anywhere.
You can also include samples in different programming languages. In Figure 3, we switched to use the Alexandria
gem in Ruby.
Overall, Slate is a nice way to offer clean documentation. There are, however, alternatives that will require less work from us.
API Blueprint is a superset of Markdown. It is a description language made specifically for web APIs.
You can define your data structures:
# Data Structures
## Book (object)
+ id: 1 (number, required)
+ title: Master Ruby Web APIs (string)
+ author (Author) - Author of the book.
## Author (object)
+ id: 1 (number, required)
+ given_name: Thibault
+ family_name: Denizet
And use them to define your resources:
# Books [/books]
## Retrieve All Books [GET]
+ Response 200 (application/json)
+ Attributes (array[Book])
All the definitions you write can then be used to generate some form of documentation using one of the many tools provided by API Blueprint like Iglo or Aglio.
API Blueprint comes with many more tools that will help “supercharge” your API.
Those tools are not really “automated,” so to speak. You’ll still have stuff to do, but it’s usually going to be inside your code. The documentation will then be generated automatically.
Swagger is a famous framework for web APIs. The Swagger specification, which was recently renamed as the OpenAPI specification, represents a way to define a standard for web APIs allowing users and machines to interact with them without knowledge of the code or access to the documentation.
With Swagger, you get generated interactive documentation, client SDKs and discoverability.
In this chapter, we will only focus on the documentation part but I recommend checking out the rest of the tools coming with Swagger.
There are tools like swagger-docs that can be added to a Rails API to generate the swagger documentation. This gem will generate a configuration that can then be used in the Swagger-UI tool. This UI tool, shown in Figure 4, is very powerful and allows you to easily explore and test the API.
Sadly, the swagger-docs
gem, like most gems used to generate swagger documentation, forces you to write swagger definitions in your controller like this:
# app/controllers/books_controller.rb
class BooksController < ApplicationController
swagger_controller :books, 'Book Management'
swagger_api :index do
summary 'Fetches all Books'
notes 'This lists all the books'
param :query, :page, :integer, :optional, 'Page number'
param :query, :per, :integer, :optional, 'Page Offset'
response :unauthorized
response :bad_request
end
# Hidden Code
This tends to quickly pollute your controllers, which I really dislike. The code should be clear and easy to navigate for people working on it and its quality should not be reduced for the sake of documentation. That’s why I prefer to keep code and documentation separated, but that’s only my opinion and you’re free to use whatever method you prefer.
Note that you can also write Swagger documentation manually with the Swagger Editor.
You can copy/paste the configuration below in the editor to play with it.
swagger: '2.0'
info:
version: '1.0.0'
title: Alexandria API
description: A sample API for Master Ruby Web APIs
host: localhost:3000
basePath: /api
schemes:
- http
consumes:
- application/json
produces:
- application/json
securityDefinitions:
alexandria_token_client_realm:
type: apiKey
in: header
name: Authorization
paths:
/books:
get:
security:
- alexandria_token_client_realm: []
description: Returns a list of books
operationId: listBooks
produces:
- application/json
parameters:
- name: fields
in: query
description: List of wanted fields for books
required: false
type: string
responses:
'200':
description: Books response
schema:
type: array
items:
$ref: '#/definitions/book'
default:
description: unexpected error
schema:
$ref: '#/definitions/errorModel'
definitions:
book:
type: object
required:
- title
- author_id
- isbn_10
- isbn_13
properties:
id:
type: integer
format: int64
title:
type: string
subtitle:
type: string
isbn_10:
type: string
isbn_13:
type: string
description:
type: string
released_on:
type: string
cover:
type: string
author_id:
type: integer
format: int64
publisher_id:
type: integer
format: int64
errorModel:
type: object
required:
- message
properties:
message:
type: string
Another approach that will require adding lots of code into your controllers is Apipie. Here is an example for the update
action in the books controller.
# app/controllers/books_controller.rb
class BooksController < ApplicationController
# Hidden code
api :PATCH, '/books/:id', 'Update a book'
param :data, Hash, desc: 'Book Data', required: true do
param :title, String, desc: 'Desc', required: true
param :subtitle, String, desc: 'Desc'
param :isbn_10, String, desc: 'Desc', required: true
param :isbn_13, String, desc: 'Desc', required: true
param :description, String, desc: 'Desc'
param :released_on, String, desc: 'Desc', required: true
param :publisher_id, Fixnum, desc: 'Desc'
param :author_id, Fixnum, desc: 'Desc', required: true
param :cover, String, desc: 'Desc'
end
error 404, 'Resource Not Found.'
error 422, 'Invalid Parameters.'
error 500, 'Something went wrong.'
formats ['json']
description "
Long Description
"
example '
# Sample request:
{
"data": {
"title": "Master Ruby Web APIs",
"isbn_10": "1234567890",
"isbn_13": "1234567890123",
"released_on": "2016-06-30",
"author_id": 1
}
}'
def update
# Hidden Code
end
# Hidden code
Just like Swagger, this will require adding a lot of code in your controllers.
Don’t let documentation pollute your code.
I worked on an API that was using Apipie. I got so annoyed by the pollution of the documentation for each method that I moved all the Apipie definitions to modules in a documentation/
folder.
In the end, I only had to load the module before the controller method and everything worked well. If you’re interested, I did something like this:
# app/controllers/documentation/books/update.rb
module Documentation::Books::Update
extend ActiveSupport::Concern
included do
api :PATCH, '/books/:id', 'Update a book'
param :data, Hash, desc: 'Book Data', required: true do
param :title, String, desc: 'Desc', required: true
param :subtitle, String, desc: 'Desc'
param :isbn_10, String, desc: 'Desc', required: true
param :isbn_13, String, desc: 'Desc', required: true
param :description, String, desc: 'Desc'
param :released_on, String, desc: 'Desc', required: true
param :publisher_id, Fixnum, desc: 'Desc'
param :author_id, Fixnum, desc: 'Desc', required: true
param :cover, String, desc: 'Desc'
end
error 404, 'Resource Not Found.'
error 422, 'Invalid Parameters.'
error 500, 'Something went wrong.'
formats ['json']
description "
Long Description
"
example '
# Sample request:
{
"data": {
"title": "Master Ruby Web APIs",
"isbn_10": "1234567890",
"isbn_13": "1234567890123",
"released_on": "2016-06-30",
"author_id": 1
}
}'
end
end
# app/controllers/books_controller.rb
class BooksController < ApplicationController
# Hidden code
include Documentation::Books::Update
def update
# Hidden Code
end
# Hidden Code
end
I was finally able to see the code.
RESTful API Modeling Language (RAML) is another option to define your API specification. It’s supposed to accompany you from design to sharing, and is characterized as machine readable while also being human friendly. RAML relies on YAML for the definition of API specifications.
Here is an example documenting the /api/books
resources from Alexandria.
#%RAML 0.8
---
title: e-BookMobile API
baseUri: http://api.e-bookmobile.com/{version}
version: v1
/api:
/books:
get:
description: "Get a list of books"
queryParameters:
fields:
description: "Specify wanted fields"
required: false
type: string
example: 'id,title,subtitle'
embeds:
description: "Specify wanted Related Entities"
required: false
type: string
example: 'author,publisher'
q:
sort:
dir:
page:
per:
responses:
200:
body:
application/json:
example: |
"data": [
{
"id": "1",
"title" "Master Ruby Web APIs"
}
]
post:
/{id}:
get:
patch:
delete:
/download:
get:
In this chapter, we reviewed a few ways to document a web API. There are more options, and you just have to find the one that suits you best. I personally tend to prefer writing documentation with Markdown. However, using YAML to write machine-friendly documentation has some clear advantages to generate not only human-friendly documentation, but also SDKs and tests.