66

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.

Rémi Becheras
  • 14,902
  • 14
  • 51
  • 81
Jun Thong
  • 669
  • 1
  • 5
  • 6

8 Answers8

17

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

Michal Miky Jankovský
  • 3,089
  • 1
  • 35
  • 36
17

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.

Félix Brunet
  • 1,993
  • 1
  • 18
  • 22
  • 1
    This has the major advantage of including all of the methods/properties defined on `express.Request`. The downside is that you can't easily document e.g. each request body property individually like you can with dotted notation shown in the other answers. I haven't figured out a way to achieve both. – ZachB Jun 07 '19 at 22:57
  • 5
    `@param {import('express').Request} req` too works if not have express as dependency – Junior Usca Oct 20 '20 at 16:23
9

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.

Jeff McMahan
  • 1,324
  • 12
  • 15
8

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){
}
Steven Spungin
  • 27,002
  • 5
  • 88
  • 78
  • Is documenting the parameter members this way an official suggestion? – Leo Sep 03 '19 at 19:38
  • I just copied your answer as an answer to my own question here: https://stackoverflow.com/questions/57771798/how-do-i-jsdoc-parameters-to-web-request/57777864#57777864 – Leo Sep 03 '19 at 19:44
7

[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

Jack Yu
  • 2,190
  • 1
  • 8
  • 14
1

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
 */
baao
  • 71,625
  • 17
  • 143
  • 203
  • 1
    If a function doesn't explicitly return a value, it implicitly returns `undefined`. Depending on the style guide you follow, this may or may not be explicitly called out with a @return tag: `@return {undefined}`. Google's Closure Compiler style guide forbids it, for example. https://developers.google.com/closure/compiler/docs/js-for-compiler#tags – Jeff McMahan Apr 04 '16 at 16:54
  • `Object` means `any` – RedGuy11 Jun 09 '21 at 11:10
0

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

mjk
  • 2,443
  • 4
  • 33
  • 33
Nick
  • 156
  • 1
  • 11
  • Middleware typically return `undefined` - many developers treat the return tag as optional or improper when the function does not return a value. – Jeff McMahan Apr 04 '16 at 16:59
0

I was trying to see how to do this in 2022. And I ended up doing the following based on the current highest voted answer:

import { Request, Response, NextFunction } from "express";

/**
 * Some Middleware
 * @param {Request} req
 * @param {Response} res
 * @param {NextFunction} next
 * @returns
 */
export function someMiddleware(req, res, next) {
  ...
}

I'm using VSCode, and it detects the req, res, and next objects. I hope this helps someone!