Postman Collection to Swagger UI Documentation
Tags: node, javascript, postman, swagger
Published: 2020-12-29 16:11:44

## Project Goal 🎯
In this article, we demonstrate how to convert documentation given as a [Postman Collection](https://www.postman.com/collection/) (that is hosted online) to a Swagger formatted documentation ([OpenAPI Specification](https://swagger.io/docs/specification/about/)), that is real-time updated according to the Postman Collection given in the beginning. 

To visualize and interact with the documentation we use [Swagger UI](https://swagger.io/tools/swagger-ui/). 

The technology the project is based on is [Node.js](https://nodejs.org/en/about/).

---

Besides the differences in the User Interface and the basic features between Swagger UI and Postman, there is another reason why we might want to use the former tool.

At the time of writing, a Postman link is only a snapshot of your collection and you need to create a new link for the most up to date version (*source* [Postman Docs](https://learning.postman.com/docs/collaborating-in-postman/sharing/#sharing-by-link)). At least that is what is true about the free version, while there might be a solution to this limitation using Postman's Pro API (https://support.postman.com/hc/en-us/articles/212510625-How-do-my-team-members-update-a-collection-link-that-I-created-).

With our implementation, if you are provided with a stable URL for accessing the documentation, you can always have the most up-to-date view of the API docs on Swagger UI, using only Node.js.

## Specific Application 🎩
The idea for this project was born from studying the [API documentation of skroutz.gr](https://developer.skroutz.gr/api/v3/), a Comparison Shopping Engine and e-commerce marketplace.

The API was given in JSON as a Postman collection in the following [link](https://developer.skroutz.gr/assets/misc/skroutz_postman_collection.json).

So what we wanted to achieve was:
* to be able to visualize the documentation using Swagger UI
* our Swagger UI documentation to be updated periodically according to the JSON Postman collection that [skroutz.gr](https://www.skroutz.gr/) initially provided (see [here](https://developer.skroutz.gr/assets/misc/skroutz_postman_collection.json))

## Packages 📦
Shortly, the packages we used for this project are:
* [express](https://www.npmjs.com/package/express) - a minimalist web framework for node
* [nodemon](https://www.npmjs.com/package/nodemon) - automatically restarts the node application when files change in the directory 
* [swagger-ui-express](https://www.npmjs.com/package/swagger-ui-express) - serves swagger-ui generated API docs from express
* [node-fetch](https://www.npmjs.com/package/node-fetch) - a module to make HTTP requests
* [fs](https://www.npmjs.com/package/fs) - access and interaction with the file system
* [api-spec-transformer](https://www.npmjs.com/package/api-spec-transformer) - helps to convert between different API specifications
* [yamljs](https://www.npmjs.com/package/yamljs) - a JavaScript YAML Parser & Encoder
* [dotenv-safe](https://www.npmjs.com/package/dotenv-safe) - ensures that all necessary environment variables are defined


## Let's see some code 🐱‍💻
As a beginner in Node.js myself, I advise you, if you are not aware of it, to study first a bit how Async Programming works in this particular programming language (suggested material: https://blog.risingstack.com/node-hero-async-programming-in-node-js/).

As you can see below, in our *app.js* file, we used the express package for our API framework and the swagger-ui-express package to produce API docs from express, based on a swagger.json or swagger.yaml file type.

```
const express = require("express");
const swaggerUi = require("swagger-ui-express");
const ymlfile = require("./documentation")

const app = express();

// load env variables and create .env.example file
const dotenv_safe = require("dotenv-safe");
dotenv_safe.config();

// middleware
app.use('/api', swaggerUi.serve, swaggerUi.setup(ymlfile));

// listening on environment port if defined or 8080
const port = process.env.PORT || 8080;
app.listen(port, () => {
    console.log(`Node JS API is listening on port: ${port}`);
});
```

In our other JS file, *documentation.js*, is where we create our *ymlfile*, which we give as an input to swaggerUi in *app.js*.

First, we need to query (fetch) periodically the URL where the JSON formatted API documentation is and store it locally into a JSON file. We do this with our *createJSONFile* async function:

```
const fetch = require('node-fetch');
const fs = require('fs').promises;
const transformer = require('api-spec-transformer');
const YAML = require('yamljs');

// set a timeout so that we can periodically query the website where the JSON formatted API documentation is
// currently set at 6 hours
setTimeout(createJSONFile, 21600)

async function createJSONFile() {
  try {
    console.log("ORDER OF EXECUTION: 7")

    const response = await fetch('https://developer.skroutz.gr/assets/misc/skroutz_postman_collection.json')
    const json = await response.json()

    await fs.writeFile("./docs/skroutz_api.json", JSON.stringify(json));
    console.log("The JSON file was saved!");

  } catch (error) {
    console.log(error.response.body);
  }

  console.log("ORDER OF EXECUTION: 8")
 
};
```

After that, we convert the JSON/Postman formatted file of the documentation to the YAML/OpenAPI Specification/Swagger format and store it locally, while also creating *ymlfile*.

```
(async function createYAMLFile() {
  const autoToSwagger = new transformer.Converter(transformer.Formats.AUTO, transformer.Formats.SWAGGER);
  
  console.log("ORDER OF EXECUTION: 1")

  autoToSwagger.loadFile("./docs/skroutz_api.json", function(err) {
    if (err) {
      console.log(err.stack);
      return;
    }

    console.log("ORDER OF EXECUTION: 4")
  
    autoToSwagger.convert('yaml')
      .then(function(convertedData) {
        // convertedData is a swagger YAML string
        // console.log(convertedData);

        console.log("ORDER OF EXECUTION: 6")

        fs.writeFile("./docs/skroutz_api.yaml", convertedData, function(err) {
          if(err) {
              return console.log(err);
          }     
        });
        console.log("The YAML file was saved!");
  
      })
      .catch(function(err){
        console.log(err);
      });

      console.log("ORDER OF EXECUTION: 5")
  });

  console.log("ORDER OF EXECUTION: 2")
  
})();

console.log("ORDER OF EXECUTION: 3")
const ymlfile = YAML.load('./docs/skroutz_api.yaml');

module.exports = ymlfile
```

Finally, in order to avoid using anything else but Node.js we do a little trick to always keep the Swagger UI dynamically up to date. In the *scripts* in our *package.json* file, we use the nodemon package to start our application, since every time a file changes locally nodemon restarts the server. Otherwise, even if our JSON and YAML files were updated, their latest version would not be served by our */api* route. As another possible solution, check out "Modify swagger file on the fly before load" at https://www.npmjs.com/package/swagger-ui-express.

In package.json:

```
 "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "npm install",
    "start": "nodemon app.js",
    "dev": "nodemon app.js"
  },
```

---

Some useful reads:
* [Three Ways to Retrieve JSON from the Web using Node.js](https://dev.to/isalevine/three-ways-to-retrieve-json-from-the-web-using-node-js-3c88)
* [5 Ways to Make HTTP Requests in Node.js using Async/Await](https://www.twilio.com/blog/5-ways-to-make-http-requests-in-node-js-using-async-await)
* [Node.js Async Best Practices & Avoiding the Callback Hell](https://blog.risingstack.com/node-js-async-best-practices-avoiding-callback-hell-node-js-at-scale/)


## Running the project ⚙️
You can find all the above code on the project's GitHub: {% github KAUTH/Swagger-Skroutz-API no-readme %}

In order to run the project, first, clone the repository from GitHub, e.g. ```git clone https://github.com/KAUTH/Swagger-Skroutz-API.git```

To run this project locally you need to have [npm installed](https://www.npmjs.com/get-npm).

When deploying the project for the first time, install all the required packages by running the ```npm install``` command on a terminal in the root directory.

After that, to run the project, use the ```npm start``` command.

The Swagger UI API documentation will then be accessible from http://localhost:8080/api/.

*Important:* There is 1 .env file (with environment variables) that our project uses, which is not on the repository for security reasons (as a best practice). In order for the project to run properly, you have to create your .env file in the same directory (root directory) that the .env.example file (example of how our .env file looks like) is present in this repository.

## Enjoy 🎈

The project is deployed with Heroku and can be found online at http://bit.ly/swagger-skroutz.

Thanks for reading, leave a like ❤️🦄🔖 if you found the article interesting and of course your feedback 📝!