Open API Generator is a tool that generates OpenAPI documentation and validates requests using Joi. It also provides an endpoint for viewing the OpenAPI documentation in both Swagger UI and JSON formats.
Install with yarn
yarn add @comparaonline/openapi-generator
Install with npm
npm install @comparaonline/openapi-generator
import { SwaggerConfig, setupOpenApi } from '@comparaonline/openapi-generator';
const swaggerConfig: SwaggerConfig = {
swaggerDoc: {
openapi: '3.0.0',
basePath: '/api/v1',
info: {
title: 'My API',
version: '1.0.0',
description: ' ',
contact: {
name: 'Insurance Core Team',
url: 'https://comparaonline.com',
email: 'info@comparaonline.com'
},
termsOfService: 'https://comparaonline.com',
license: {
name: 'ComparaOnline',
url: 'https://comparaonline.com'
}
},
servers: [
{
url: 'http://localhost:4000',
description: 'Development server'
},
{
url: 'https://devserver.com',
description: 'Staging server'
},
{
url: 'https://productionserver.com',
description: 'Production server'
}
]
},
folders: [
'./entities/*.ts'
],
jsonPath: './docs/swagger.json',
endpoint: '/open-api',
active: true
}
setupOpenApi(swaggerConfig)
import express, { Router } from "express"
import { productsRouter } from "./products.router"
import { usersRouter } from './users.router'
import { runSwagger } from '@comparaonline/openapi-generator';
const app=express()
const router=Router()
router.get('/',(_req,res)=>{
res.status(200).send('Hello World!')
})
router.use('/products',productsRouter)
router.use('/users', usersRouter)
app.use('/api/v1/',router)
//added here
runSwagger(app,router)
const port=process.env.PORT ?? 3000
app.listen(port,()=>console.log(`APP listening on port ${port}`))
-- The "joi" should be assimilated to the way in which express receives the "request" It must contain the structure --
- body
- params
- query
//Products Router Example
import joi from 'joi'
import { Router } from 'express';
import { createHandler } from '@comparaonline/openapi-generator';
//Joi Schema
import joi from 'joi'
import { Router } from 'express';
import { createHandler } from './openapi.config';
const schema=joi.object().unknown().required().keys({
params: {
id: joi.number()
}
})
const productsRouter=Router()
productsRouter.get('/search/:id',createHandler(schema),(_req,res,_next)=>{
//your code here
res.status(200).send('OK')
})
export {productsRouter}
The joi will not only validate that your parameters are correct but will also generate the documentation
To add query params you must follow the same logic as for path params in this way, the following example contains path and query params
const schema=joi.object().unknown().required().keys({
params: {
id: joi.number()
},
query:{
search:joi.string().required()
}
})
productsRouter.get('/search/:id',createHandler(schema),(_req,res,_next)=>{
//your code here
res.status(200).send('OK')
})
To add headers you must follow the same logic as for path and query params in this way, the following example contains path , query params and headers
const schema = joi.object().unknown().required().keys({
params: {
id: joi.number()
},
query: {
search: joi.string()
},
headers: joi.object().unknown().keys({
"api-key": joi.string().uuid().required()
})
})
productsRouter.get('/search/:id',createHandler(schema),(_req,res,_next)=>{
//your code here
res.status(200).send('OK')
})
The result is something like this
- If we want to obtain the raw json to import it into "postman" for example, we simply need to add ".json" to the url /api/v1/open-api.json
{"openapi":"3.0.0","info":{"title":"My API","version":"1.0.0","description":" ","contact":{"name":"Insurance Core Team","url":"https://comparaonline.com","email":"info@comparaonline.com"},"termsOfService":"https://comparaonline.com","license":{"name":"ComparaOnline","url":"https://comparaonline.com"}},"servers":[{"url":"http://localhost:3000/api/v1","description":"Development server"},{"url":"https://devserver.com/api/v1","description":"Staging server"},{"url":"https://productionserver.com/api/v1","description":"Production server"}],"paths":{"/":{"get":{"operationId":"/_get","description":"","responses":{"200":{"description":""}},"tags":[""]}},"/products/search/{id}":{"get":{"operationId":"/products/search/{id}_get","description":"","responses":{"200":{"description":""}},"tags":["products"],"parameters":[{"in":"header","name":"api-key","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"search","required":false,"schema":{"type":"string"}},{"in":"path","name":"id","required":false,"schema":{"type":"number","format":"float"}}]}}},"components":{"schemas":{"ExampleResponse":{"type":"object","properties":{"name":{"type":"string"}},"additionalProperties":false}}}}
To document the responses we must configure in the swaggerConfig the routes where the classes that refer to the response schemas are located and we need to use a different signature than "createHandler".
{
//Other configuration ...
folders: [
'./entities/*.ts'
]
//In this case the swagger will add the schemas corresponding to all the classes found within './entities/*.ts'
}
We can use any of the following two signatures.
interface ResponseType {
type: //must be a class
statusCode: StatusCodes
description?: string
array?: boolean
}
interface Params {
schema: ObjectSchema | undefined
contentType?: string
responseType: ResponseType
description?: string
operationId?: string
}
function createHandler (params: Params): RequestHandlerWithDocumentation
function createHandler (joi: ObjectSchema, responseType: ResponseType): RequestHandlerWithDocumentation
Example: We have two endpoints, one to list users and another to search by id The first returns an array of users, the other a user object /users/list /users/:id
export class User{
firstName:string
lastName:string
}
import joi from 'joi'
import { Router } from 'express';
import { createHandler } from '@comparaonline/openapi-generator';
import { User } from './entities/user';
const findOneSchema = joi.object().unknown().required().keys({
params: {
id: joi.number()
},
headers: joi.object().unknown().keys({
"api-key": joi.string().uuid().required()
})
})
const findAllSchema = joi.object().unknown().required().keys({
headers: joi.object().unknown().keys({
"api-key": joi.string().uuid().required()
})
})
const usersRouter = Router()
usersRouter.get('/list', createHandler(findAllSchema,{statusCode:200,type:User,array:true}), (_req, res, _next) => {
//your code here
const users=[new User(),new User()]
res.status(200).send(users)
})
usersRouter.get('/:id', createHandler(findOneSchema,{statusCode:200,type:User}), (_req, res, _next) => {
//your code here
const user=new User()
res.status(200).send(user)
})
export { usersRouter }
This will allow us to add additional information to our endpoints and is the most complete way to use
- responseType
- schema
- operationId
- description
- contentType
const searchSchema = joi.object().unknown().required().keys({
headers: joi.object().unknown().keys({
"api-key": joi.string().uuid().required()
}),
query:{
name:joi.string()
}
})
// Add search endpoint
usersRouter.get(
'/search',
createHandler(
{
schema: searchSchema,
responseType: {
statusCode: 200,
type: User,
array: true,
description: 'All users that match the query'
},
description:'Search by name',
operationId:'searchByName',
contentType:'application/json'
}), (_req, res, _next) => {
//your code here
const users = [new User(), new User()]
res.status(200).send(users)
})