npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@comparaonline/openapi-generator

v1.0.4

Published

Soap and rest connector

Downloads

201

Readme

Open API Generator

@comparaonline/openapi-generator

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.

Installation

Install with yarn

  yarn add @comparaonline/openapi-generator

Install with npm

  npm install @comparaonline/openapi-generator

Initialization

Create a config file and exports {runSwagger,createHandler}



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: '[email protected]'
      },
      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)

Add runSwagger to app and main router in the main file

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}`))

Use the "createHandler" with joi to validate and document the "request"

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

Query Params

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')
})

Headers

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

  • This is the "endpoint" indicated in the configuration /api/v1/open-api Swagger

Swagger opened

  • 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":"[email protected]"},"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}}}}

Responses

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'
}

How do we add responses to the handler?

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

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

The "User" class inside one of the folders indicated in the configuration

export class User{
    firstName:string
    lastName:string
}

The User's router

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 }

FindOne Find One User FindAll Find All User Schemas Schemas

Complete requestHandler signature

This will allow us to add additional information to our endpoints and is the most complete way to use

  • responseType
  • schema
  • operationId
  • description
  • contentType

Example


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)
})

Search Search Complete