Document Nodejs API with Swagger
Damian Piwowarczyk
Posted on January 21, 2022
What is an API?
API documentation is a technical manual, containing information on how to consume API and how to use it. Documentation also describes what type of data format API is expecting in request and what types are returned from it.
Why do we need API documentation?
As with every technology, there must be a guide to help understand others how to use it. API documentation helps people do understand what type of operation can be performed and what resources they can be accepted & retrieved. Swagger will help us to make our API easy to ready and test some of the functionality.
Today we will focus on creating a simple REST API and integrating swagger with Open API 3.0 specification. Our documentation will be available in a graphical form accessible through the browser and downloadable JSON format file. JSON file could be imported later to the API testing tool or Postman.
For this example, we will use nodejs with express.
What our API documentation will consist of?
- Docs for GET, POST, PUT, DELETE
- Description of resources
- Endpoints and methods
- Schema of Request/Response
- Datatypes and parameters accepted
- Examples
Lets get started!
├── controllers
│ └── hero.controller.js
├── index.js
├── package.json
├── routes
│ ├── hero.routes.js
│ └── index.js
└── swagger.js
In our directory npm init
npm install express swagger-jsdoc swagger-ui-express
In package.json we will add
"type":"module"
to enable ES6 modules.
In index.js
we create basic express app and import our swagger config.
import express from 'express'
import router from './routes/index.js'
import swaggerDocs from './swagger.js'
const app = express()
const port = 5000
app.use(express.json())
app.use(router)
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
swaggerDocs(app, port)
})
swagger.js
contains our configuration.
import swaggerJsdoc from 'swagger-jsdoc'
import swaggerUi from 'swagger-ui-express'
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'Hero API',
description: 'Example of CRUD API ',
version: '1.0.0',
},
},
// looks for configuration in specified directories
apis: ['./routes/*.js'],
}
const swaggerSpec = swaggerJsdoc(options)
function swaggerDocs(app, port) {
// Swagger Page
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec))
// Documentation in JSON format
app.get('/docs.json', (req, res) => {
res.setHeader('Content-Type', 'application/json')
res.send(swaggerSpec)
})
}
export default swaggerDocs
Now we setup our API routes along with OpenAPI specification represented in YAML format. All fields in the specification are case-sensitive. For better accessibility & readability we will place specifications just above the API routes.
mkdir routes && cd routes
index.js
import express from 'express'
import heroRoutes from './hero.routes.js'
const router = express.Router()
/**
* @openapi
* /healthcheck:
* get:
* tags:
* - Healthcheck
* description: Returns API operational status
* responses:
* 200:
* description: API is running
*/
router.get('/healthcheck', (req, res) => res.sendStatus(200))
router.use(heroRoutes)
export default route
Our Hero API will consist of 4 requests.
GET, POST, PUT, DELETE
For each of requests we will write specification that will let API users know what type of input our API expect and what it returns along with response codes.
We will keep our logic separately in controllers directory.
import express from 'express'
import {
getHeroesHandler,
addHeroHandler,
deleteHeroHandler,
editHeroHandler,
} from '../controllers/hero.controller.js'
const router = express.Router()
/**
* @openapi
* '/api/heroes':
* get:
* tags:
* - Hero
* summary: Get all heroes
* responses:
* 200:
* description: Success
* content:
* application/json:
* schema:
* type: array
* items:
* type: object
* properties:
* id:
* type: number
* name:
* type: string
* 400:
* description: Bad request
*/
router.get('/api/heroes', getHeroesHandler)
/**
* @openapi
* '/api/hero':
* post:
* tags:
* - Hero
* summary: Create a hero
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - id
* - name
* properties:
* id:
* type: number
* default: 2
* name:
* type: string
* default: New Hero Name
* responses:
* 201:
* description: Created
* 409:
* description: Conflict
* 404:
* description: Not Found
*/
router.post('/api/hero', addHeroHandler)
/**
* @openapi
* '/api/hero':
* put:
* tags:
* - Hero
* summary: Modify a hero
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - id
* - name
* properties:
* id:
* type: number
* default: 1
* name:
* type: string
* default: Hulk
* responses:
* 200:
* description: Modified
* 400:
* description: Bad Request
* 404:
* description: Not Found
*/
router.put('/api/hero', editHeroHandler)
/**
* @openapi
* '/api/hero/{id}':
* delete:
* tags:
* - Hero
* summary: Remove hero by id
* parameters:
* - name: id
* in: path
* description: The unique id of the hero
* required: true
* responses:
* 200:
* description: Removed
* 400:
* description: Bad request
* 404:
* description: Not Found
*/
router.delete('/api/hero/:id', deleteHeroHandler)
export default router
Next we will create functions responsible for handling incoming requests and returning appropriate data.
hero_controler.js
let heroes = [
{
id: 1,
name: 'Batman',
},
{ id: 2, name: 'Spiderman' },
]
export async function getHeroesHandler(req, res) {
res.status(200).json(heroes)
}
export async function addHeroHandler(req, res) {
if (heroes.find((hero) => hero.id === req.body.id)) {
res.status(409).json('Hero id must be unique')
}
else{
heroes.push(req.body)
res.status(200).json(heroes)
}
}
export async function deleteHeroHandler(req, res) {
const index = heroes.findIndex((hero) => hero.id == req.params.id)
if (index >= 0) {
heroes.splice(index, 1)
res.status(200).json(heroes)
} else res.status(400).send()
}
export async function editHeroHandler(req, res) {
const index = heroes.findIndex((hero) => hero.id == req.body.id)
if (index >= 0) {
heroes.splice(index, 1, req.body)
res.status(200).json(heroes)
} else res.status(400).send()
Now we can start our API with node index.js
and navigate to localhost:4000/docs
to view our documentation or we can also get JSON format from localhost:4000/docs.json
which can be used for testing or perhaps imported to POSTMAN.
To sum up, this is only a simple demo to demonstrate how to use swagger with OpenAPI 3.0 in our express application. I think the swagger is a great tool because it helps us to create clear and neat documentation, provide an excellent visual page for users who could effortlessly and quickly test API functionality themselves.
I hope this article was helpful to some of you guys. Thanks for reading!
Github repo
Posted on January 21, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.