What is Swagger?

Swagger is an open-source tool that allows you to describe the structure of your APIs so that machines can read them. Why is it so great? Well, by reading your API’s structure, it can automatically build beautiful and interactive API documentation. It also helps in testing the API’s endpoints.

This project is available here.

In this blog, we will document 5 RESTful API endpoints using swagger. These endpoints are described below:


URL HTTP Verb Action
/api/fruits GET Returns all fruits
/api/fruits/:_id GET Returns a single fruit
/api/fruits POST Add a fruit
/api/fruits/:_id PUT Update a Fruit
/api/fruits/:_id DELETE Delete a Fruit

Motivation

It becomes really difficult for new employees to understand RESTFUL API documentation written in plain text by some other employee. Thus motivating me to do this project.

Goal

My goal with this project is to implement a frontend based API documentation which is easily understandable by everyone and as a result, the API testing becomes easy and efficient.

Glossary

REST: Representational state transfer (a software architectural style that defines a set of constraints to be used for creating Web services).

Schema: A schema defines the structure of the document, default values, validators, etc.

YAML: It stands for YAML Ain’t Markup Language, a recursive acronym, to distinguish its purpose as data-oriented, rather than document markup.

Project Requirements

  1. Basic programming fundamentals are a must
  2. Must have a little bit idea about Node.js and JavaScript

Step by Step Tutorial

1. This project uses MongoDB, so the first step is to create a fruits schema as defined below

const mongoose = require("mongoose");
const fruitsSchema = new mongoose.Schema({
name: {
type: String
}
});
module.exports = fruits = mongoose.model("fruits", fruitsSchema)
view raw fruits.js hosted with ❤ by GitHub

I am using only a single property (i.e name) so that the project remains short and informative.

2. The second step for API documentation is to generate the swagger specification

The swagger specification is generated inside the server.js file with the help of an npm package namely swagger-jsdoc .
After generating the swagger specification we have to set up and serve it with swagger-ui-express.
This is the server.js file below:

require('./config/config')
const express = require('express')
const mongoose = require('mongoose');
mongoose.set('useFindAndModify', false);
const app = express()
const bodyParser = require('body-parser')
const swaggerJsonDoc = require('swagger-jsdoc')
const swaggerUI = require('swagger-ui-express')
const swaggerOptions = {
swaggerDefinition: {
info: {
title: "Documenting REST API's",
description: "This is an implementation of how to document your RESTful API's using SWAGGER",
servers: ['http://localhost:3000']
},
"components": {
"schemas": {
"fruits": {
"properties": {
"name": {
"type": "string"
}
}
}
}
}
},
apis: ['./routes/api/fruits.js']
}
const swaggerDocs = swaggerJsonDoc(swaggerOptions)
app.use('/api-docs', swaggerUI.serve, swaggerUI.setup(swaggerDocs))
app.use(bodyParser.urlencoded({
extended: false
}))
app.use(bodyParser.json())
app.use("/api", require('./routes/api/fruits.js'))
mongoose.connect(process.env.URLDB, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
}).then(response => {
console.log('Mongo DB Connected')
}).catch(error => {
console.log(error)
})
app.listen(process.env.PORT, () => {
console.log(`Server started on ${process.env.PORT}`)
})
view raw server.js hosted with ❤ by GitHub

Let us understand the code block by block, in order to get a better understanding of swagger.

The first block includes the requiring of the npm packages so that these packages can be used inside our application.

The second block includes the definition of swagger options which are automatically converted into swagger docs(swagger specification) with the help of swagger-jsdoc package.
The swagger options also consist of two parts: swagger definition and APIs
swagger definition: The info part contains the title, description, and the server on which the app will be running. The components part consist of all other things like various schemas used in the project (fruits in our case)
APIs: This is an array of paths of different APIs.
After this, serve the swagger specification at /api-docs endpoint.

After this run the server in order to check if swagger is successfully set-up or not, you should see something like this:

How to test and document your API's using Swagger in

const swaggerOptions = {
swaggerDefinition: {
info: {
title: "Documenting REST API's",
description: "This is an implementation of how to document your RESTful API's using SWAGGER",
servers: ['http://localhost:3000']
},
"components": {
"schemas": {
"fruits": {
"properties": {
"name": {
"type": "string"
}
}
}
}
}
},
apis: ['./routes/api/fruits.js']
}
const swaggerDocs = swaggerJsonDoc(swaggerOptions)
app.use('/api-docs', swaggerUI.serve, swaggerUI.setup(swaggerDocs))
view raw swagger.js hosted with ❤ by GitHub

3. The third and final step is to create the routes and update the route handlers(the most important step towards interactive API documentation)

swagger-jsdoc uses JSDoc-type comments to generate the Swagger specification. So, add such comments, in YAML, to the route handlers that describe their functionality

/**
* @swagger
* /api/fruits:
* get:
* tags:
* - Fruits
* description: Returns all fruits
* produces:
* - application/json
* responses:
* 200:
* description: An array of fruits
* schema:
* $ref: '#/components/schemas/fruits'
* 500:
* description: SERVER ERROR
*/
router.get('/fruits', async(req, res) => {
try {
var fruits = await Fruits.find();
//console.log(fruits)
return res.json({
fruits
})
} catch (err) {
res.status(500).send('Server error')
}
})
view raw yaml.js hosted with ❤ by GitHub

This is pretty much self explanatory. We have an /api/fruits endpoint that returns two possible responses 200(successful) and 500(server error) to our GET request (you can add more like 401 error for unauthorized access)
$ref is used to refer to the response schema (defined in the components sections while generating swagger specs.) .

Similarly add such comments for other RESTful API’s also. Refer the code below :

const express = require("express");
const router = express.Router();
var Fruits = require('../../model/fruits.js')
/**
* @swagger
* /api/fruits:
* get:
* tags:
* - Fruits
* description: Returns all fruits
* produces:
* - application/json
* responses:
* 200:
* description: An array of fruits
* schema:
* $ref: '#/components/schemas/fruits'
* 500:
* description: SERVER ERROR
*/
router.get('/fruits', async(req, res) => {
try {
var fruits = await Fruits.find();
//console.log(fruits)
return res.json({
fruits
})
} catch (err) {
res.status(500).send('Server error')
}
})
/**
* @swagger
* /api/fruits/{_id}:
* get:
* tags:
* - Fruits
* description: Returns a single fruit
* produces:
* - application/json
* parameters:
* - name: _id
* description: Particular Fruit Object's ID (Automatically assigned by MongoDB)
* in: path
* required: true
* type: string
* responses:
* 200:
* description: A single fruit
* schema:
* $ref: '#/components/schemas/fruits'
* 500:
* description: Server Error
*/
router.get('/fruits/:_id', async(req, res) => {
try {
var objectId = req.params._id;
var fruit = await Fruits.findOne({
"_id": objectId
});
if (fruit) {
return res.json(fruit)
} else {
return res.send(`No fruit with the ${objectId} ID is present in the database`)
}
} catch (err) {
console.log(err)
res.status(500).send('Server error')
}
})
/**
* @swagger
* /api/fruits:
* post:
* tags:
* - Fruits
* description: Adds a new fruit to the database
* produces:
* - application/json
* parameters:
* - name: fruit
* description: Fruit object
* in: body
* required: true
* schema:
* $ref: '#/components/schemas/fruits'
* responses:
* 200:
* description: Successfully added
* 500:
* description: Server error
*/
router.post('/fruits', async(req, res) => {
try {
var name = req.body.name;
if (name) {
var fruit = new Fruits({
"name": name
})
fruit.save();
return res.send(`Added ${name} to the DB`)
} else {
return res.send('Enter the name of the fruit in the body')
}
} catch (err) {
res.status(500).send('Server Error')
}
})
/**
* @swagger
* /api/fruits/{_id}:
* put:
* tags:
* - Fruits
* description: Updates a single fruit
* produces:
* - application/json
* parameters:
* - name: fruit
* description: Fruit object resources
* in: body
* required: true
* schema:
* $ref: '#/components/schemas/fruits'
* - name: _id
* description: Fruit Object ID
* in: path
* required: true
* responses:
* 200:
* description: Successfully added
* 500:
* description: Server error
*/
router.put('/fruits/:_id', async(req, res) => {
try {
var objectId = req.params._id;
var name = req.body.name;
if (objectId && name) {
await Fruits.findByIdAndUpdate({
"_id": objectId
}, req.body);
return res.json({
"status": "Successully updated"
})
} else {
res.send('Check your inputs!!')
}
} catch (err) {
res.status(500).send('Server error');
}
})
/**
* @swagger
* /api/fruits/{_id}:
* delete:
* tags:
* - Fruits
* description: Deletes a single fruit
* produces:
* - application/json
* parameters:
* - name: _id
* description: Fruit Object ID
* in: path
* required: true
* responses:
* 200:
* description: Successfully deleted
* 500:
* description: Server error
*/
router.delete('/fruits/:_id', async(req, res) => {
try {
var objectId = req.params._id;
if (await Fruits.findOne({
"_id": objectId
})) {
await Fruits.findByIdAndDelete({
"_id": objectId
})
return res.json({
"status": "Successfully deleted"
})
} else {
return res.send(`No fruit with ${objectId} ID exists in the database`)
}
} catch (err) {
res.status(500).send("Server error")
}
})
module.exports = router
view raw yaml.js hosted with ❤ by GitHub

With this step, our coding part is complete, so now in order to run and test it, Run the command node server.js and visit localhost:3000/api-docs to view the API documentation with Swagger UI. Here you can test your API’s by clicking on the respective API .

How to test and document your API's using Swagger in Node.js

Learning Tools and Strategies

  1. Don’t write messy code, everything should be well understood. As a result, it will help you in debugging.
  2. Do a console.log() at each major step you feel is important or you feel maybe will throw some kind of error so that you are able to get the result of each major code-snippet.
  3. Another thing is to go through the documentation of swagger-jsdoc and swagger-ui-express thoroughly.

Search terms

test, api, document, swagger, interactive api documentation

Workaround

I prefer working in a quiet environment so that I am completely focused

Reflective Analysis

Learning how to use swagger was a fun learning experience. It is an amazing tool that has makes the understanding and testing of API endpoints easy. A very common error that is expected while using swagger is the indentation of the comments, so be careful with that.

Conclusion

This project covers the documentation and testing of 5 basic RESTful API endpoints without authorizing and authenticating the user. How about a complete project that also includes authorization? Let me know your thought below on the comment section. Overall, I feel swagger is a great tool to do interactive documentation of your API’s endpoint. Refer to the documentation for some help.

This project is available here.