Creating API Documentation with Swagger on NodeJS
Tags: node, typescript, beginners, backend
Published: 2024-07-13 03:00:00

Introduction
------------

**Swagger** is a popular, simple, and user-friendly tool for creating **APIs**. Most backend developers, regardless of the programming languages they use, are familiar with **Swagger**. This article will guide you through creating API documentation using **Swagger** on **Node.js** (specifically integrated with the **Express** framework). This is handy when you want to provide API documentation in a professional UI format for stakeholders involved in integration.

![NodeJS Swagger](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fs4pe4us61xrf3qncgig.png)

Restful API
-----------

**REST** stands for **Representational State Transfer**. It is an architectural style that defines a set of constraints for creating web services. **RESTful APIs** provide a simple and flexible way to access web services without complex processing.

Common HTTP Methods in **RESTful APIs**:

\- **GET**: Used to read (retrieve) a representation of a resource. It returns data in XML or JSON format.

\- **POST**: Creates new resources or subordinates to existing ones.

\- **PUT**: Updates existing resources or creates new ones if the client chooses the resource ID.

\- **PATCH**: Partially modifies resources, describing changes rather than sending the entire resource.

\- **DELETE**: Removes a resource.

  

Building API and API Documentation with Swagger
-----------------------------------------------

In this example, we'll use **Node.js, Express, and Swagger**. First, let's set up a **Node.js** project and install the necessary packages:

```sh
yarn add express swagger-jsdoc swagger-ui-express
yarn add -D @types/swagger-jsdoc @types/swagger-ui-express
```

Create a file named **_swagger.ts_** with Swagger document configuration as follows:

```ts
import * as swaggerJsdoc from 'swagger-jsdoc'
import * as swaggerUi from 'swagger-ui-express'

const options = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'Employee API',
      description: 'Example of CRUD API ',
      version: '1.0.0',
    },
  },
  apis: ['./router/*.ts'], // path to routers
}

const swaggerSpec = swaggerJsdoc(options)

export function swaggerDocs(app, port) {
  app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec))
  app.get('/docs.json', (req, res) => {
    res.setHeader('Content-Type', 'application/json')
    res.send(swaggerSpec)
  })
}
```

Next up is the **_index.ts_** file for integrating **Express** and **Swagger**.

```ts
import * as express from 'express'
import router from './router'
import {swaggerDocs} from './swagger'

const app = express()
const port = 3000
app
  .use(express.json())
  .use(router)
  .listen(port, () => {
    console.log(`Listening at http://localhost:${port}`)
    swaggerDocs(app, port)
  })
```

To set up the **API** routers, I'll create a file named router.ts and define the API documentation right above the routers, identified by the keyword **_@openapi_**.

The APIs will include 4 methods: **GET, POST, PUT, DELETE**.

```ts
import * as express from 'express'
import {addEmployeeHandler, deleteEmployeeHandler, editEmployeeHandler, getEmployeesHandler} from './controller'

const router = express.Router()

/**
 * @openapi
 * '/api/employees':
 *  get:
 *     tags:
 *     - Employee
 *     summary: Get all employee
 *     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/employees', getEmployeesHandler)

/**
 * @openapi
 * '/api/employee':
 *  post:
 *     tags:
 *     - Employee
 *     summary: Create a employee
 *     requestBody:
 *      required: true
 *      content:
 *        application/json:
 *           schema:
 *            type: object
 *            required:
 *              - id
 *              - name
 *            properties:
 *              id:
 *                type: number
 *              name:
 *                type: string
 *     responses:
 *      201:
 *        description: Created
 *      409:
 *        description: Conflict
 *      404:
 *        description: Not Found
 */
router.post('/api/employee', addEmployeeHandler)

/**
 * @openapi
 * '/api/employee':
 *  put:
 *     tags:
 *     - Employee
 *     summary: Modify a employee
 *     requestBody:
 *      required: true
 *      content:
 *        application/json:
 *           schema:
 *            type: object
 *            required:
 *              - id
 *              - name
 *            properties:
 *              id:
 *                type: number
 *              name:
 *                type: string
 *     responses:
 *      200:
 *        description: Modified
 *      400:
 *        description: Bad Request
 *      404:
 *        description: Not Found
 */
router.put('/api/employee', editEmployeeHandler)

/**
 * @openapi
 * '/api/employee/{id}':
 *  delete:
 *     tags:
 *     - Employee
 *     summary: Remove employee by id
 *     parameters:
 *      - name: id
 *        in: path
 *        description: The unique id of the employee
 *        required: true
 *     responses:
 *      200:
 *        description: Removed
 *      400:
 *        description: Bad request
 *      404:
 *        description: Not Found
 */
router.delete('/api/employee/:id', deleteEmployeeHandler)

export default router
```

I'll also provide an additional file named **_controller.ts_**, implemented in a simple way to handle API requests.

```ts
import {Request, Response} from 'express'

let employees = [
  {id: 1, name: 'Name 1'},
  {id: 2, name: 'Name 2'},
]

export function getEmployeesHandler(req: Request, res: Response) {
  res.status(200).json(employees)
}

export function addEmployeeHandler(req: Request, res: Response) {
  if (employees.find(employee => employee.id === req.body.id)) {
    res.status(409).json('Employee id must be unique')
  } else {
    employees.push(req.body)
    res.status(200).json(employees)
  }
}

export function deleteEmployeeHandler(req: Request, res: Response) {
  const index = employees.findIndex(employee => employee.id === +req?.params?.id)
  if (index >= 0) {
    employees.splice(index, 1)
    res.status(200).json(employees)
  } else {
    res.status(400).send()
  }
}

export function editEmployeeHandler(req: Request, res: Response) {
  const index = employees.findIndex(employee => employee.id == req.body.id)
  if (index >= 0) {
    employees.splice(index, 1, req.body)
    res.status(200).json(employees)
  } else {
    res.status(400).send()
  }
}
```

After successfully starting the project, you can access the page **_[http://localhost:3000/docs](http://localhost:3000/docs)_** to view the API documentation in UI format generated by Swagger.

![RestFul API Swagger](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wcje649g6uxojmg3ige.png)

Conclusion
----------

API Documentation is one of the crucial documents that **back-end** developers provide to stakeholders to facilitate integration (such as **front-end** teams).

Due to the popularity and convenience of **Restful API** and **Swagger**, it's an indispensable tool for creating API documentation in a simple, professional, and consistent UI format.

**_If you found this content helpful, please visit [the original article on my blog](https://howtodevez.blogspot.com/2024/04/creating-api-documentation-with-swagger-on-nodejs.html) to support the author and explore more interesting content._**

<a href="https://howtodevez.blogspot.com/2024/03/sitemap.html" target="_blank" rel="noreferrer"><img src="https://img.shields.io/badge/Blogger-FF5722?style=for-the-badge&logo=blogger&logoColor=white" width="36" height="36" alt="Blogspot" /></a><a href="https://dev.to/chauhoangminhnguyen" target="_blank" rel="noreferrer"><img src="https://img.shields.io/badge/dev.to-0A0A0A?style=for-the-badge&logo=dev.to&logoColor=white" width="36" height="36" alt="Dev.to" /></a><a href="https://www.facebook.com/profile.php?id=61557154776384" target="_blank" rel="noreferrer"><img src="https://img.shields.io/badge/Facebook-1877F2?style=for-the-badge&logo=facebook&logoColor=white" width="36" height="36" alt="Facebook" /></a><a href="https://x.com/DavidNguyenSE" target="_blank" rel="noreferrer"><img src="https://img.shields.io/badge/X-000000?style=for-the-badge&logo=x&logoColor=white" width="36" height="36" alt="X" /></a>