Documenting APIs using ApiDoc.js

By Júlia Rocha 5 min read

Learn how to write and generate elegant API documentation.

Introduction to ApiDoc.js

It is good practice to provide detailed documentation about how client applications can connect to and consume data from an API. The best part is that we will use a simple tool that generates documentation from the code’s comments.

ApiDoc.js is a Node.js CLI module to generate the documentation.

You can install it as a global module to enable the new apidoc command in your terminal, so run this command:

npm install apidoc - g

Building an API

Obs.: In our example, we’ll use the Express web framework to build an API, and to simplify things, we won’t write any business rules in the routes; we’ll create empty routes so we can write the necessary documentation.

So first, let’s create the task-api project and install the express module:

mkdir task - api
cd task - api
npm init
npm install express--save

To build our API code, let’s create the index.js:

var express = require('express');
var app = express();

// Serving static files from "public" folder
app.use(express.static('public'));

app.get('/tasks', function(req, res) {
    // business logic for list all tasks...
});
app.get('/tasks/:id', function(req, res) {
    // business logic for find a task...
});
app.post('/tasks', function(req, res) {
    // business logic for create a task...
});
app.put('/tasks/:id', function(req, res) {
    // business logic for update a task...
});
app.delete('/tasks/:id', function(req, res) {
    // business logic for delete a task...
});
app.listen(3000, function() {
    console.log('Task api up and running...');
});

The app.use(express.static(‘public’)) middleware enables a static server for the public folder; we’ll use this directory to store all generated documentation files.

Documenting all API routes

Now we can write documentation; you just need to use comments and the params provided by apidoc, which you can see in the docs.

To start our documentation process, first, you need to create a descriptor file called apidoc.json in the root folder with these attributes:

{
    "name": "Task API documentation",
    "version": "1.0.0",
    "description": "API task list manager",
    "template": {
        "forceLanguage": "en"
    }
}

The template.forceLanguage disables the browser language detection; in this case, it will force the English language.

Now, let’s start the documentation process, by editing the index.js, route by route. The first will be the app.get(‘/tasks’) function, and in this route, we’re gonna use the following params:

  • @api: HTTP method, the path address, and the route’s title;
  • @apiGroup: route group name;
  • @apiSuccess: describes the fields and their data types for a successful response;
  • @apiSuccessExample: shows an output sample about a successful response.
  • @apiErrorExample: shows an output sample about a failed response.

Have a look:

/**
 * @api {get} /tasks List all tasks
 * @apiGroup Tasks
 * @apiSuccess {Object[]} tasks Task's list
 * @apiSuccess {Number} tasks.id Task id
 * @apiSuccess {String} tasks.title Task title
 * @apiSuccess {Boolean} tasks.done Task is done?
 * @apiSuccess {Date} tasks.updated_at Update's date
 * @apiSuccess {Date} tasks.created_at Register's date
 * @apiSuccessExample {json} Success
 *    HTTP/1.1 200 OK
 *    [{
 *      "id": 1,
 *      "title": "Study",
 *      "done": false
 *      "updated_at": "2016-02-10T15:46:51.778Z",
 *      "created_at": "2016-02-10T15:46:51.778Z"
 *    }]
 * @apiErrorExample {json} List error
 *    HTTP/1.1 500 Internal Server Error
 */
app.get('/tasks', function(req, res) {
    // business logic for listing all tasks...
});

The next route, the app.get(‘/tasks/:id’), will use all params from the previous one and one more param:

  • @apiParam: describes the fields and their data types for a path parameter;
/**
 * @api {get} /tasks/:id Find a task
 * @apiGroup Tasks
 * @apiParam {id} id Task id
 * @apiSuccess {Number} id Task id
 * @apiSuccess {String} title Task title
 * @apiSuccess {Boolean} done Task is done?
 * @apiSuccess {Date} updated_at Update's date
 * @apiSuccess {Date} created_at Register's date
 * @apiSuccessExample {json} Success
 *    HTTP/1.1 200 OK
 *    {
 *      "id": 1,
 *      "title": "Study",
 *      "done": false
 *      "updated_at": "2016-02-10T15:46:51.778Z",
 *      "created_at": "2016-02-10T15:46:51.778Z"
 *    }
 * @apiErrorExample {json} Task not found
 *    HTTP/1.1 404 Not Found
 * @apiErrorExample {json} Find error
 *    HTTP/1.1 500 Internal Server Error
 */
app.get('/tasks/:id', function(req, res) {
    // business logic for finding a task...
});

In the app.post(‘/tasks’), it will be used the @apiParam and @apiParamExample, explain how to send data via a body request, and the @apiSuccess {Boolean} done=false Task is done? is different from the others, because the done=false means default values for a field, in this case, the done field.

/**
 * @api {post} /tasks Register a new task
 * @apiGroup Tasks
 * @apiParam {String} title Task title
 * @apiParamExample {json} Input
 *    {
 *      "title": "Study"
 *    }
 * @apiSuccess {Number} id Task id
 * @apiSuccess {String} title Task title
 * @apiSuccess {Boolean} done=false Task is done?
 * @apiSuccess {Date} updated_at Update date
 * @apiSuccess {Date} created_at Register date
 * @apiSuccessExample {json} Success
 *    HTTP/1.1 200 OK
 *    {
 *      "id": 1,
 *      "title": "Study",
 *      "done": false,
 *      "updated_at": "2016-02-10T15:46:51.778Z",
 *      "created_at": "2016-02-10T15:46:51.778Z"
 *    }
 * @apiErrorExample {json} Register error
 *    HTTP/1.1 500 Internal Server Error
 */
app.post('/tasks', function(req, res) {
    // business logic for creating a task...
});

To write the app.put(‘/tasks/:id’) and app.delete(‘/tasks/:id’) route documentation, there is no secret and no new params to explain, so take a look at how both will be written:

/**
 * @api {put} /tasks/:id Update a task
 * @apiGroup Tasks
 * @apiParam {id} id Task id
 * @apiParam {String} title Task title
 * @apiParam {Boolean} done Task is done?
 * @apiParamExample {json} Input
 *    {
 *      "title": "Work",
 *      "done": true
 *    }
 * @apiSuccessExample {json} Success
 *    HTTP/1.1 204 No Content
 * @apiErrorExample {json} Update error
 *    HTTP/1.1 500 Internal Server Error
 */
app.put('/tasks/:id', function(req, res) {
    // business logic for update a task
});

/**
 * @api {delete} /tasks/:id Remove a task
 * @apiGroup Tasks
 * @apiParam {id} id Task id
 * @apiSuccessExample {json} Success
 *    HTTP/1.1 204 No Content
 * @apiErrorExample {json} Delete error
 *    HTTP/1.1 500 Internal Server Error
 */
app.delete('/tasks/:id', function(req, res) {
    // business logic for deleting a task
});

Let’s generate the docs! To do it, just run the commands below:

apidoc - e "(node_modules|public)" - o public / apidoc
node index.js

The apidoc command filters all files except those in the node_modules and public folders, using the flag -e “(node_modules|public)” to ignore these directories, and it generates all files in the public/apidoc folder. After running these commands, you will be able to access the address: http://localhost:3000/apidoc.

This time, we have a complete documentation page that describes, step by step, how to create a client application to consume data from the API. Take a look at the image below:

api-documented

Conclusion

Now you have a well-documented API, which will allow other developers to create client-side applications using the API according to the API documentation.

Before deploying your commercial or enterprise JavaScript apps to production, make sure you are protecting their code against reverse-engineering, abuse, and tampering with Jscrambler.

Start your free trial today!