Generate documentation with @novice1/api-doc-generator: Part 1
Tags: javascript, beginners, node, typescript
Published: 2024-01-03 00:26:50

In the previous articles, we went through:

- how to create a Rest API with [@novice1/app](https://www.npmjs.com/package/@novice1/app)
- how to validate request data with [@novice1/validator-joi](https://www.npmjs.com/package/@novice1/validator-joi)

Now we wish to use the API for ourself or give an access for other people to use. For that we are missing something of fundamental importance: **documentation**.

Let us be real, during my years as a developer, I never met anyone who liked writing documentation. That does not mean that it's not necessary to do so.
For a REST API, a schema of the API can be very useful and self explanatory. There are multiple schema specifications like [OpenAPI](https://swagger.io/specification/) and [Postman](https://schema.postman.com/). **[@novice1/api-doc-generator](https://www.npmjs.com/package/@novice1/api-doc-generator)** covers them both.

With **[@novice1/api-doc-generator](https://www.npmjs.com/package/@novice1/api-doc-generator)**, you can easily generate an **OpenAPI** and/or **Postman** schema from your application. That package is quite complete, covers all cases and has a [reference sheet](https://kisiwu.github.io/novice-api-doc-generator/latest/index.html) for its usage.

In this article we will see how to generate an OpenAPI schema with **[@novice1/api-doc-generator](https://www.npmjs.com/package/@novice1/api-doc-generator)** for a simple REST API.

## Create the service

Let's install the dependencies
```sh
npm i @novice1/api-doc-generator yamljs
npm i --save-dev @types/yamljs
```
and create the service that will generate the documentation

_src/services/openapi/generator.ts_
```ts
import { OpenAPI } from '@novice1/api-doc-generator';
import { EXTERNAL_PORT, EXTERNAL_PROTOCOL, HOSTNAME } from '../../config/server';

export const openAPIGenerator = new OpenAPI()
    .setTitle('API DOC')
    .setHost(`${EXTERNAL_PROTOCOL}://${HOSTNAME}${EXTERNAL_PORT ? ':'+EXTERNAL_PORT : ''}`)
    .setConsumes(['application/json'])
    .setTags([
        {
            name: 'default',
            externalDocs: { description: 'Find more info here', url: 'https://swagger.io/specification/' }
        }
    ])
```

## Access documentation

Using @novice1/app, the app provides metadata. Those are used to generate the REST API schema.

_src/index.ts_
```ts
import logger from '@novice1/logger';
import { app } from './app';
import { httpError, httpNotFound } from './middlewares/http';
import { PORT } from './config/server';
import { openAPIGenerator } from './services/openapi/generator';

// 404
app.use(httpNotFound);

// error
app.useError(httpError);

// start server
app.listen(PORT, () => {
    logger.info('Application running on port', PORT)

    /**
     * Add app metadata to API documentation generator
     */
    openAPIGenerator.add(app.meta)
})
```

Create a route that renders the schema:

_src/routes/documentation.ts_
```ts
import routing from '@novice1/routing';
import Joi from 'joi';
import YAML from 'yamljs';
import { openAPIGenerator } from '../services/openapi/generator';

export default routing().get({
    path: '/documentation',
    parameters: {
        // query
        query: Joi.object().keys({
            json: Joi.boolean().default(false)
        }),
        // do not put this route in the documentation 
        undoc: true
    }
}, (req, res) => {
    // Get the JSON schema
    const result = openAPIGenerator.result()

    if (!req.query.json) {
        // respond with a yaml schema
        res.set('Content-Type', 'text/plain')
        res.send(YAML.stringify(result, 10, 2))
    } else {
        // respond with the JSON schema
        res.json(result)
    }
})
```

And register it with the other routers:

_src/routes/index.ts_
```ts
import corsOptions from './cors-options';
import documentation from './documentation';
import helloWorld from './hello-world';
import items from './items';

// all routers
export default [
    corsOptions,
    documentation,
    helloWorld,
    items
]
```

Now, we run the application to see if we have access to the schema at http://localhost:8080/documentation.

For development:

```sh
npm run dev
```

For production:

```sh
npm run lint
npm run build
npm start
```

It should render the schema in yaml format. To get the json format, we can set the following query string in the url `?json=true`.

The schema can be copied and pasted to the [Swagger Editor](https://editor.swagger.io/) to get a UI to test your API.

## Render swagger UI

If you want your API to not just serve the schema but also the UI, you can install the following dependencies:

```sh
npm i swagger-ui-express
npm i --save-dev @types/swagger-ui-express
```

and change the route that serves the documentation like so:

_src/routes/documentation.ts_
```ts
import routing, { RequestHandler } from '@novice1/routing';
import Joi from 'joi';
import YAML from 'yamljs';
import swaggerUi from 'swagger-ui-express';
import { openAPIGenerator } from '../services/openapi/generator';

const swaggerUIController: RequestHandler = (req, res, next) => {
  const swaggerDocument = openAPIGenerator.result()
  return swaggerUi.setup(swaggerDocument)(req, res, next)
}

const swaggerRouter = routing()
  .get({
    path: '/',
    parameters: {
      undoc: true
    },
  },
  swaggerUIController)
  .get({
    path: '/schema',
    parameters: {
      query: Joi.object().keys({
        json: Joi.boolean().default(false),
      }).options({ allowUnknown: true, stripUnknown: false }),

      undoc: true
    }
  }, (req, res) => {
    const result = openAPIGenerator.result();

    if (!req.query.json) {
      res.set('Content-Type', 'text/plain');
      res.send(YAML.stringify(result, 10, 2));
    } else {
      res.json(result);
    }
  });

export default routing().use('/documentation', 
  swaggerUi.serve, 
  swaggerRouter
);
```

That way, your API will serve the UI _(html)_ at `/documentation` and the schema _(yaml/json)_ at `/documentation/schema`.



As I said earlier, **[@novice1/api-doc-generator](https://www.npmjs.com/package/@novice1/api-doc-generator)** is quite complete. We can define everything there is in [OpenAPI Specification 3.0.3](https://swagger.io/specification/v3/) and [Postman Collection Format v2.1.0](Postman Collection Format v2.1.0) so it would be impossible to cover it all in one article. As of today, the best way to learn **@novice1/api-doc-generator** is by reading the package's documentation that you can find [here](https://kisiwu.github.io/novice-api-doc-generator/latest/index.html). It lists and defines the classes, interfaces, enumerations and gives a few examples on how to define [authorizations](https://kisiwu.github.io/novice-api-doc-generator/latest/index.html#md:auth), [responses](https://kisiwu.github.io/novice-api-doc-generator/latest/index.html#md:responses), etc ... 
I would gladly talk about more aspects of the package in future articles if it is requested in the comment section. 
What is sure is that we will continue using and updating our REST API documentation in the next article that will be about **Authorizations with @novice1/app**.

## References

You can see the result of what we have done till now [right here on Github](https://github.com/shygyver/nodejs-tuto/tree/part-03).