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

swagger-scrape

v1.1.8

Published

Interfaces with JSDoc and custom doc tags to generate a complete Swagger Document

Downloads

5

Readme

Swagger Scraper

The purpose of this module is to generate Swagger documentation from the JSDoc tags you have annotated your endpoint handlers with. It is able to interpret complex types as well.

Feedback is welcome.

How to initialise

You'll need an ExpressJS application. Find the point at which you have initialised the endpoints.

Required packages:

  • express-list-endpoints: ^4.0.0
  • swagger-ui-express: ^4.0.2

The following code will initialise the swagger documentation endpoints:

let appInfo = {
    version: "1.0.0",
    title: "Project Title",
    description: "Project Description",
    common: [
    	/* files that contains jsdoc common across the endpoints */
    ]
};


// assumed: app = the express js application instance

// include this dependency
const swaggerScrape = require('swagger-scrape');

// grab all endpoint information of endpoints registered with express js
let swaggerEndpoints = swaggerScrape.scrapeEndpoints("./", app);

// conver the endpoint information to a swagger document
let swaggerDoc = swaggerScrape.toSwaggerJson(appInfo, "hostname", "/application-context", swaggerEndpoints);

// expose the swagger ui based on the swagger doc at '/api-docs'.
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDoc));

Reading multiple endpoint maps

In certain situations you will require multiple endpoint maps to be parsed and converted into Swagger documentation. One example being when using a solution that allows both publicEndpoints and protected endpoints. Use the code below to allow for that:

const swaggerHost = process.env.SWAGGER_HOST || ('localhost:' + DEFAULT_PORT);
const swaggerEndpoints = SwaggerScrape.scrapeEndpoints("./", LambdaConfig.endpoints, SwaggerScrape.lambdaRoutes);
const publicSwaggerEndpoints = SwaggerScrape.scrapeEndpoints("./", LambdaConfig.publicEndpoints, SwaggerScrape.lambdaRoutes);
const swaggerDoc = SwaggerScrape.toSwaggerJson(appInfo, `${swaggerHost}`, "/", [...publicSwaggerEndpoints, ...swaggerEndpoints]);

The code is scraped twice, and the resulting arrays in swaggerEndpoints and publicSwaggerEndpoints are combined when invoking the toSwaggerJson function.

Annotating your code

What follows is a completely annotated function that produces a proper swagger document:

/**
 * @swagger
 *
 * Just put the complete description here. It is the description that is shown when you click into a 
 * swagger endpoint in the interface.
 *
 * @summary This is the short text shown right next to the endpoint name
 * @tag TestEndpoint
 *
 * @param req       the request object
 * @param resp      the reponse object
 *
 * @param pathParameter {number} (path) part of the url
 * @param queryParam	{string} (query) part of the requestquery (?queryParam=blaat)
 * @param headerParam	{string} (header) expected header value from request headers
 * @param bodyModel 	{ExpectedModel} (body) This is a complex type parameter that has a proper type hint.
 *
 * @response 403    You cannot go here.
 * @response 200    {ExpectedModel} all is well, information is returned in complex type.
 */
module.exports = function(req, resp) {

	// your code here.

};

Basics

To indicate that this jsdoc pertains to swagger documentation, add the @swagger tag.

You can organise your swagger endpoints into different categories by specifying one or more @tag.

Specify a short summary by adding the @summary tag.

Parameters

Parameters have the following notation:

@param <name> (query|path|header|body) {type} description

You can add an asterisk in front of the parameter type to indicate that it is required, like:

@param myParam *(query) {string} a required parameter

If you do not specify a type, string is assumed.

The following primitive types are supported:

  • number
  • boolean
  • integer
  • string
  • object
  • file

Anything else is considered to be a complex type and has additional logic to retrieve information for.

Response

Specifying a swagger response can be done by using the following notation:

@response <statusCode> {type} description

If no type is specified string is assumed. You can specify a complex type here as well.

Complex Types

When you specify a type name that is not a primitive, the swagger scraper uses the same file to try and find a constant of the name specified as the type name that was annotated with a @model jsdoc tag.

Fields inside the constant definition can be annotated with:

  • @type {name}; the type of this parameter
  • @required; if the parameter is required.

Find a nested model definition below:

/**
 * @model
 */
const AddressModel = {

    /**
     * @type {string}
     */
    street : '',

    /**
     * @type {number}
     */
    zipcode: 1010

}

/**
 * @model
 *
 * This is the empty response model
 */
const ExpectedModel = {

    /**
     * @type {number}
     * @required
     */
    age : 0,

    /**
     * @type {string}
     * @required
     */
    name: null,

    /**
     * @type {string[]}
     */
    lastNames : null,

    /**
     * @type {AddressModel}
     */
    address: null


};

Notice how you can specify arrays by appending the typename with [].

Also note how the AddressModel is another complex type. The scraper recursively resolves them in the same document.

You can also use the @typedef notation described here:

  • https://jsdoc.app/tags-typedef.html

Hinting at where the documentation lives

Oftentimes when initialising endpoints into ExpressJS, you simply refer to a variable that is a closure or function with the signature:

function(req, resp) {}

Given the dynamic nature of Javascript, we cannot easily discover what file the function handler lives in (something we need to know if we would like JSdoc to parse a file). So, for that reason the @fileHint mechanism was introduced.

Inside your function, hint at where the handler actually lives:

app.get('/profile', (req, resp) => {
	/* @fileHint: src/controllers/MyProfile.js::showProfile; */
	return MyProfile.actionFunction(req, resp);
});

or:

app.get('/profile', (req, resp) => {
	/* @fileHint: src/controllers/MyProfile.js; */
	return MyProfile.actionFunction(req, resp);
});

The notation for filehint is as follows:

/* @fileHint: <filename>(::<id-name>); */

The filename parameter is relative to the basepath passed into the scrapeEndpoints function (./ in our example). There is an optional parameter called <id-name>. If you have a file that has more than one handler inside it, you can specify the @id <id-name> it will go looking for. Otherwise it will find the first @swagger annotated jsdoc block.

An example of this would be:

module.exports = {

	/**
	 * @id showProfile
	 * @swagger
	 * This bit of documentation is specific to the actionFunction method
	 */
	function actionFunction(req, resp) {
		
	}

	/**
	 * @id anotherAction
	 * @swagger
	 * This bit of documentation is specific to the other method
	 */
	function actionFunction2(req, resp) {
		
	}


}