Swagger – REST documentation made easy

Standard

Up until now, REST service documentation was really painful job (believe me, I have burnt my hands with JAX-RS, JAX-WS). So called XML generated by REST hosted servers was partially helpful to understand the operations and skeleton of data structure. Beyond that, the separate documentation in order to understand the sample values and detailed information about operation was still required. Well that’s not all, even if one wants to try out the API, there was need of having separate client. Well, for all this time we continued to create beautiful documentation manually, which definitely was plagued with problems like syncing, version maintenance etc. Luckily, the problem seems to be fading away with the framework, I came across in recent times – Swagger.

Swagger is NodeJS package and pretty easy to get started. The framework has its own CLI and you can easily boot project using few commands. NodeJS itself is blessed by the community with beautiful modules like grunt, gulp, karma etc. which definitely relieves developer’s life and if you are not leveraging all that, your productivity is definitely questionable. Anyways, lets get started with Swagger. The below paragraphs illustrate usage of Swagger with Simple TODO application and also offers a convenient way of modularizing, monolith yaml definition for Swagger.

To install Swagger, just execute following command at the command prompt

npm install -g swagger

Above command should result in installation of swagger in global node modules directory. Now switch to the directory, where swagger project must be created. Make sure you execute following command

swagger project create todo

Swagger will require input like what framework should be used to create this project.

Swagger create project

Create Project

I have chosen, express as the desired framework. In case if you are unable to interact with the options, try using different cmd softwares. The problem typically appears on Windows 10 machines (Just like my hybrid laptop/tablet). You can use options like cmder. The Console Emulator has got nice typeface along with color combination. Anyways, the step should result in creation of the project and you should get default swagger.yaml file with sample controller.
Swagger project structure

Swagger project structure

. The swagger definition (swagger.yaml) should be available under the directory – api/swagger/swagger.yaml. The default definition should include everything inside swagger.yaml. This is good enough to get started, but soon you will realize that maintaining one file for entire project (Enterprise) is not a feasible option. The file must be broken into multiple and luckily Swagger offers an option to do the same. More details about specification can be found on OpenAPI.

To execute the project created, just hit the following command at the root of the project(henceforth all the commands should be executed at the root of the project). If all the dependencies are downloaded properly, you should see a message indicating a URL to be hit to see the result.

swagger project start

Hit the URL in the browser and you should see a message printing “Hello, Scott!”. Well we are now good to get started with defining operations for TODO app. At the end of this post, we should have services for Creating TODO, Marking TODO complete, delete TODO and get all TODOs. The implementation doesn’t specify authentication strategy to be used (but you can use any implementation you want). Let’s just begin, by installing one more swagger module.

npm install swagger-tools --save-dev

Swagger-tools provides a useful feature of presenting REST services in readable format. The feature also includes provision to invoke service(s). Once the installation is complete, modify app.js to include following code.

//..Declare variable just above the config variable.
var SwaggerUi = require('swagger-tools/middleware/swagger-ui');
//...Inside SwaggerExpress.create add following line.
app.use(SwaggerUi(swaggerExpress.runner.swagger));
app.use("/api/swagger", express.static("api/swagger"));

Notice the mapping of api/swagger folder as static files. It is required to serve split yaml definition files to swagger ui. Without this declaration, the UI will render the documentation partially(I ended up wasting 2 days in figuring out this problem). This should be sufficient to wire /doc url to our application. Start the project using

swagger project start

The project should start without any errors and you should be able to visit the documentation at http://localhost:10010/docs. You should see following screen.

Swagger Project Documentation

Swagger Project Documentation


You should see various options like List, Expand Operation, fields to accept input for service, sample schema structure, input parameter details, output structure, error details etc. Go ahead, fill the details and click on Try it out! button. You should see the response from service.

Lets start defining operations for our TODO app. Create 3 folders as models, parameters and paths inside api\swagger folder. As the name suggests, the folders are used for defining models, parameters for a request and service path respectively. Inside api\swagger\path folder, create a new file todo.yaml. This file will be used to define all the operations related to TODO services. Add the following code inside the file. Make sure the spaces are entered properly.

x-swagger-router-controller: todo
description: TODO services
put:
  tags:
    - todo
  description: Creats new TODO entry.
  summary: Create new TODO.
  operationId: create
  consumes:
    - application/json
  produces:
    - application/json
  parameters:
    - $ref: "../parameters/todo-parameters.yaml#/TodoEntry"
  responses:
    "200":
      description: Successful creation of TODO.
      schema:
        $ref: "../models/todo-models.yaml#/NewEntryResponse"
    default:
      description: Error
      schema:
        $ref: "../models/todo-models.yaml#/TodoErrorResponse"
get:
  tags:
    - todo
  description: Retrieves all TODO created using service.
  summary: Get all TODOs.
  operationId: getAll
  consumes:
    - application/json
  produces:
    - application/json
  responses:
    "200":
      description: List of All TODOs
      schema:
        type: array
        items:
          $ref: "../models/todo-models.yaml#/Todo"
    default:
      description: Error
      schema:
        $ref: "../models/todo-models.yaml#/TodoErrorResponse"
delete:
  tags:
    - todo
  description: Deletes an entry from TODO collection.
  summary: Delete TODO entry
  operationId: deleteEntry
  consumes:
    - application/json
  produces:
    - application/json
  parameters:
    - name: todoID
      type: number
      required: true
      in: query
      description: ID of the TODO to be deleted.
  responses:
    "200":
      description: Successfull deletion of TODO
      schema:
        $ref: "../models/todo-models.yaml#/DeleteEntryResponse"
    default:
      description: Error
      schema:
        $ref: "../models/todo-models.yaml#/TodoErrorResponse"

Note that how each of the parameters and responses are externalized to separate files. The references to various files are relative to the current definition. todo.yaml file will be referenced in main swagger.yaml file. Open swagger.yaml file and enter following entry before /swagger declaration. Note that the indentation is important.

  /todo:
    $ref: api/swagger/paths/todo.yaml

Note the reference to the todo.yaml file is not relative to the swagger.yaml, but it is relative to the root of the project and as per the static folder, we have mapped earlier in app.js file. Create new file todo-models.yaml under api\swagger\models folder and define following models in it.

Todo:
  type: object
  properties:
    id:
      type: number
    title:
      type: string
NewEntryResponse:
  required:
    - message
  properties:
    message:
      type: string
DeleteEntryResponse:
  required:
    - message
  properties:
    message:
      type: string
TodoErrorResponse:
  required:
    - message
  properties:
    message:
      type: string

Above definition will create reusable set of models that can be referenced in any yaml definition. The last part is to declare the parameters. Create new file todo-parameters.yaml file inside api\swagger\parameters folder and copy following contents in it.

TodoEntry:
  name: todoTitle
  type: string
  required: true
  in: body
  description: Title for todo task.

Thats all and we are done with services definition. Note, I have added a tag todo, which can be declared in swagger.yaml for tagging the service definitions (you will find more details in github repository). Create todo.js file inside api\controllers folder and copy following contents in it.

module.exports = {
  create: create,
  getAll: getAll,
  deleteEntry: deleteEntry
};

var todos= [];

function create(req, res) {
  var todoTitle = req.swagger.params.todoTitle.value;
  var todoID = todos.length + 1;
  todos.push({'title':todoTitle, 'id': todoID});
  res.json({message:"Task created successfully."});
}

function getAll(req, res){
  res.json(todos);
}

function deleteEntry(req, res){
  var todoID = req.swagger.params.todoID.value;
  for(var i=0; i< todos.length; i++){
    if(todos[i].id==todoID){
      todos.splice(i, 1);
      break;
    }
  }
  res.json({message:"TODO deleted successfully."});
}

And we are done. Just verify the yaml definitions by executing swagger project verify command and if there are no errors, you are ready to test your service. Just enter swagger project start command and you should be able to browse the fully documented REST service. I have hosted entire project at github. Feel free to clone it and modify the contents for further experiments. Make sure to execute npm install to install all the dependencies.

TODO service using swagger

TODO service using swagger

Be Sociable, Share!

Leave a Reply

Your email address will not be published. Required fields are marked *