Document Nodejs API with Swagger
Tags: javascript, webdev, tutorial, node
Published: 2022-01-21 15:29:55

### 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.

![Image api_doc](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rg6ymmu4xk8fwpncxxxt.png)

### 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 


![Image api_documentation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hg8pt39vi23bxs6lpzks.png)

Lets get started!
```bash
├── 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
```json
"type":"module"
```
to enable ES6 modules.

In `index.js` we create basic express app and import our swagger config.

```javascript
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.
```javascript
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`
```javascript
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.



```javascript
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`
```javascript
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](https://github.com/przpiw/CRUD_API_swagger)