Contents
Since more than 30 years I have to write good documentation. On the university I used to write documents with TeX and LaTeX because of the high quality output. The more professional the output is, the more professional the content looks to the reader. LaTeX offers a lot of possibilities to style your document so the reading can be fun. But LaTeX isn’t that simple. In my current projects I searched for a documentation tool that is wide spread, free and simple. The answer was Asciidoctor for me.
Document structure
The task is to write an API documentation and produce a typical HTML and PDF document as the output. This kind of documentation should be well structured because it is not meant to read at once but to give detail information when the reader needs it.
Document header
At the beginning of the document you should place general information about the document like
- title
- author
- date of creation
Here is my default header snippet:
= API documentation for ... Bernd Kursawe <bernd.kursawe@praxisit.de> :toc: :sectanchors: :sectnums:
After the title (“API documentation for …”) the author information follows (Name <e-mail>). With “:toc:” you place a table-of-content at the beginning of the document. “:sectanchors:” sets anchors at every section and “:sectnums:” care about the numeration of the sections.
Introduction
The reader should get an idea at the beginning of the document what it’s all about. Explain keywords and the goal of the API. This should be the first section. In Asciidoctor the only thing you need to do is define a new section on the top level (2 =):
== Introduction This API is ... A person is a human beeing with a first and a last name...
Standards
Defining standards is important for the whole documentation. According to the DRY[1. Don’t repeat yourself] principle you should define common uses of HTTP verbs and response codes at the beginning of the document. Here is an example:
HTTP verbs have the following default meanings: .HTTP verbs
[width=”80%”,options=”header”,cols=”1,4″]
|======= |Verb |Default usage of HTTP verb |GET |Get some information. Select resources by path variables. Query parameters can be used for multiple search parameters. Sending a request body is not allowed. |POST |Create new resources or start jobs (not idempotent!). The data for the new resource is send in the request body. The new resource or some information about the job are given back in the response body. Don’t use query parameters. |PUT |Change a resource. To select the resource use path variables. The complete resource data has to be send in the request body and the changed data is returned in the response body. Don’t use query parameters. |DELETE |Delete a resource. The resource is identified by its id as a path variable. There should be no request body or any query parameters. A response body is not required. |======= Here are the meanings of most used HTTP response codes: .Default response codes
[width=”80%”,options=”header”,cols=”1,2,6,3″]
|======= |Code |Meaning |Description |Used with |200 |OK |Response code if everything went OK. |GET, PUT, DELETE |201 |CREATED |Successful created a new resource |CREATED |404 |NOT FOUND |The resource(s) is/are not found. |GET, PUT, DELETE |=======
Group endpoints
Try to group endpoints (RequestMappings) of the same resource. Use the resource as a section and describe the raison d’être of the resource. Every endpoint should be described in its own subsection. List all endpoints in the same order (e.g. GET, POST, PUT, DELETE). Here is a simple example:
=== Person Administers information about people. ==== Find all people include::{snippets}/person/find-all-people/method-path.adoc[] include::{snippets}/person/find-all-people/description.adoc[] ===== Response fields The response body contains a list of <<person-data>>. ===== Example request / response .Request include::{snippets}/person/find-all-people/http-request.adoc[] .Response include::{snippets}/person/find-all-people/http-response.adoc[] ==== Find a single person include::{snippets}/person/find-one-person/method-path.adoc[] include::{snippets}/person/find-one-person/description.adoc[] ===== Path parameters include::{snippets}/person/find-one-person/path-parameters.adoc[] ===== Response fields The response body contains <<person-data>>. ===== Example request / response .Request include::{snippets}/person/find-one-person/http-request.adoc[] .Response include::{snippets}/person/find-one-person/http-response.adoc[] ===== Wrong id .Request include::{snippets}/person/find-one-person-with-wrong-id/http-request.adoc[] .Response include::{snippets}/person/find-one-person-with-wrong-id/http-response.adoc[] ==== Create a new person include::{snippets}/person/create-new-person/method-path.adoc[] include::{snippets}/person/create-new-person/description.adoc[] ===== Request fields include::{snippets}/person/create-new-person/request-fields.adoc[] ===== Response fields The response body contains <<person-data>>. ===== Example request / response .Request include::{snippets}/person/create-new-person/http-request.adoc[] .Response include::{snippets}/person/create-new-person/http-response.adoc[] ==== Delete a person include::{snippets}/person/delete-person/method-path.adoc[] include::{snippets}/person/delete-person/description.adoc[] ===== Path parameters include::{snippets}/person/delete-person/path-parameters.adoc[] ===== Response fields include::{snippets}/person/delete-person/response-fields.adoc[] ===== Example request / response .Request include::{snippets}/person/delete-person/http-request.adoc[] .Response include::{snippets}/person/delete-person/http-response.adoc[] ===== Wrong id .Request include::{snippets}/person/delete-person-with-wrong-id/http-request.adoc[] .Response include::{snippets}/person/delete-person-with-wrong-id/http-response.adoc[]
Data structures
Often you use the same resource (data structure) in several endpoints. This is boring for the reader and the document size becomes longer as necessary. So add a separate section at the end. Describe all common data structures like this:
== Data types [[person-data]] === Person include::{snippets}/person/find-one-person/response-fields.adoc[]
Alternatives
Are there any alternatives to Asciidoctor? Of course, but here are some and the reasons I don’t use them:
- Microsoft Word / LibreOffice: Although WYSIWYG-editors has a lot of advantages it has drawbacks producing HTML output. In HTML everything looks a little bit different to the layout in the editor and this is at least misleading.
Another problem is the document format. It is not simple text but (proprietary) XML. When you want to use a versioning system like GIT it is difficult to see the differences between 2 versions in a structured format like XML. - LaTeX: Complicated to learn and slow processing. It offers amazing possibilities to format and style your document in PDF. But it has no advantages when used to produce HTML.
- Markdown: A ‘cousin’ of Asciidoctor with lesser functionality but also usable for API documentation. It were my second choice.