How to annotate Express middlewares with JSDoc?

JavascriptExpressWebstormJsdoc

Javascript Problem Overview


I'm trying to document an Express middleware, but the build-in validation tool in WebStorm tells me that types are incorrectly assigned in the following JSDoc block:

/**
 * My middleware.
 *
 * @param {Object} req
 * @param {Object} res
 * @param {Function} next
 * @return {Object}
 */
exports.show = function(req, res, next) {
    ...
};

In Express sources, I didn't find any @typedefs to help me. Also, I want to avoid things like @param {*}.

What is the correct way to document Express middleware using JSDoc? Thanks for any help.

Javascript Solutions


Solution 1 - Javascript

Use DefinitelyTyped

  1. Install express types npm install --save-dev @types/express
  2. use e.Response as usually @param {e.Response} res

More types

  • in file /node_modules/@types/express/index.d.ts
  • for Response it is e.Response because:

... declare namespace e { ... export interface Response extends core.Response { } ...

WebStorm

install types via Settings > Languages & Frameworks > Javascript > Libraries > @types/express

Solution 2 - Javascript

You can document your middleware with

const express = require("express");

/**
 * @param {express.Request} req
 * @param {express.Response} res
 * @param {express.NextFunction} next
 */
function (req, res, next) {}

when you have middleware that add attribute to req, you can also add them with

const express = require("express");

/**
 * @param {express.Request & {specialParam1 : string, specialParam2 : any}} req
 * @param {express.Response} res
 * @param {express.NextFunction} next
 */
function (req, res, next) {}

or event better, create a typedef for each source of new elem added on "req" and use "&" to create a type combining them all.

Solution 3 - Javascript

First, we agree that middleware are functions; no special type declaration will generally be warranted. Beyond that, middleware tend to be highly decoupled—modular—which means the @module tag is usually applicable (and this has nice consequences when you run jsdoc).

/**
 * Description of my middleware.
 * @module myMiddleware
 * @function
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
 * @param {Function} next - Express next middleware function
 * @return {undefined}
 */

The return tag is optional depending on your style guide, since middleware don't return a value. Finally, contrary to what Nick and mmm claim, the next parameter is a function.

> http://expressjs.com/en/guide/using-middleware.html > > Middleware functions are functions that have access to the request > object (req), the response object (res), and the next middleware > function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.

next isn't a fancy Express-internal concoction; Express passes each middleware function the request, the response, and the next middleware function in the stack, like this:

mw1 = function(req, res, next){}.bind(undefined, req, res, mw2)
mw2 = function(req, res, next){}.bind(undefined, req, res, mw3)

The value of next within the scope of mw1 is mw2.

Solution 4 - Javascript

You can not only JsDoc the parameter types and descriptions, and but also their expected members.

/**
 * 
 * @module myMiddleware
 * @function
 * @param req {Object} The request.
 * @param res {Object} The response.
 * @param req.params.foo {String} The foo param.
 * @param req.query.bar {String} The bar query.
 * @param req.body {Object} The JSON payload.
 * @param {Function} next
 * @return {undefined}
 */
function foo(req, res, next){
}

Solution 5 - Javascript

[2021-03-02 Update] Original answer is like 100% JSDOC + 0% typescript, but I found a 20% JSDOC + 80% typescript (pure definition) solution. In typescript github, it mentioned this method. See the final paragraph in post.

I combine other answer and modify some code,
it could include all of the methods/properties defined on express.Request and event custom request body.
It could not only use in request.body, but also support in req.query.
That because express.Request support generics, so we could use this in JSDoc.

First, remember to install @types/express with npm install --save-dev @types/express.

Second, setup like following code.

// @ts-check
/**
 * @typedef {object} showRequestBody
 * @property {string} name this is name in request body
 * @property {number} age this is age in request body
 * 
 * @typedef {object} showRequestQuery
 * @property {string} name this is name in query
 * @property {number} age this is age in query
 * 
 * @param {import('express').Request<{}, {}, showRequestBody, showRequestQuery>} req
 * @param {import('express').Response} res 
 * @param {import('express').NextFunction} next 
 */
exports.show = function(req, res, next) {
};

Note: I use it in vscode.
I leave answer here, and I hope this will help other people also have this question.


other methods/properties defined on express.Request, for example req.headers


req.body hint


req.query hint

20% JSDOC + 80% typescript

The following example doesn't need tsconfig.json or install extra tsc.
But, you couldn't use jsdoc to generate documentation.

With import + export definition

If you want to extend interface with importing some module, you need to use export in definition. Then import it in JSDOC.

enter image description here

enter image description here

Without import + export definition

If you don't want to import custom definition in JSDOC, you could just define the interface without import and export. Then, you could directly use it in JSDOC.

enter image description here

enter image description here

Extend the express module

There is another way to build custom interface, just use declare module to extend the interface. You could even define custom method.

enter image description here

enter image description here

enter image description here

Solution 6 - Javascript

req, res and next are all objects, and a middleware usually doesn't return, so the following may be used.

/**
 * My Middleware
 * @name MyMiddleWare
 * @function
 * @param {Object} req
 * @param {Object} res
 * @param {Object} next
 */

Solution 7 - Javascript

The only thing you need to change is the @param {Function} next to @param {Object}. Also, @return should describe what the function returns; e.g, (Object, Array) or a combination ({Object|Null})

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJun ThongView Question on Stackoverflow
Solution 1 - JavascriptMichal Miky JankovskýView Answer on Stackoverflow
Solution 2 - JavascriptFélix BrunetView Answer on Stackoverflow
Solution 3 - JavascriptJeff McMahanView Answer on Stackoverflow
Solution 4 - JavascriptSteven SpunginView Answer on Stackoverflow
Solution 5 - JavascriptJack YuView Answer on Stackoverflow
Solution 6 - JavascriptbaaoView Answer on Stackoverflow
Solution 7 - JavascriptNickView Answer on Stackoverflow