question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Express's Middleware type

See original GitHub issue

In my app, I’m using following error handler, which would break in case of err === null. Why is the error parameter allowed to be null in Middleware type?

export default function errorHandler(err, req, res, next) {
  const errorDetails = err.stack || err;
  ...
}

I would rather see something like this:

declare type NextFunction = () => mixed;
declare type NextFunctionWithError = (err?: Error) => mixed;
declare type Middleware =
    ((req: Request, res: Response, next: NextFunction) => mixed) |
    ((error: Error, req : Request, res: Response, next: NextFunctionWithError) => mixed);

What do you think?

Issue Analytics

  • State:open
  • Created 7 years ago
  • Reactions:3
  • Comments:25 (12 by maintainers)

github_iconTop GitHub Comments

3reactions
bassettsjcommented, Dec 1, 2016

Hmmm… this is a tough one. I think you could improve this by making sure to define that class as extending the base class and make sure that you add the type definitions for them to middleware functions:

// @flow
const app = express();
app.use(session(...));
declare class session$Request, extends express$Request {
    session: { [key: string]: mixed };
}

app.use((req: session$Request, res: express$Response, next: express$NextFunction): void => {
  req.session.user = { id: 2 };
  next();
});

I think we will probably have to add parameterization to the lib defs, but I am not sure what the best way to do this. We should look to see how definitely-typed accomplished this and see if we could follow their lead.

1reaction
jtlappcommented, Dec 3, 2016

I’ll have to look at your #508 mod more closely. I’ve pulled out most of my app-specific stuff, so you can see how I did the wrapping:

// @flow

/******************************************************************************
Express request/response extensions and supporting wrapper classes.
******************************************************************************/

var express = require('express');

/*::
declare class _SessionRequest extends express$Request
{
    // custom extensions
    
    userInfo: {
        userID: number;
        displayName: string;
        privileges: string;
    };
    
    // request internals
    
    connection: {
        remoteAddress: string;
    };
    requestTimeUTC: number;
}

declare class _SessionResponse extends express$Response
{
    // custom extensions
    
    sessionState: any; // replace 'any' with your custom representation
}

// adapted from express$NextFunction
declare type _SessionNext = (err: ?ErrnoError) => void;

// adapted from express$Middleware
declare type _SessionMiddleware =
    ((req: _SessionRequest, res: _SessionResponse, next: _SessionNext)
        => mixed) |
    ((err: ErrnoError, req: _SessionRequest, res: _SessionResponse,
        next: _SessionNext) => mixed);

declare interface _SessionRouteMethodType<T>
{
    // adapted from express$RouteMethodType
    (middleware: _SessionMiddleware, ...extra: Array<_SessionMiddleware>): T;
    (path: string|RegExp|string[], ...middleware: Array<_SessionMiddleware>): T;
}

declare interface _SessionRouterMethodType<T>
{
    // adapted from express$RouterMethodType
    (middleware: _SessionMiddleware, ...extra: Array<_SessionMiddleware>): T;
    (path: string|RegExp|string[], ...middleware: Array<_SessionMiddleware>): T;
    (path: string, router: _SessionRouter): T;
}

declare class _SessionRouter
{
    // adapted from express$Route and express$Router
    constructor(options?: express$RouterOptions): void;
    all: _SessionRouteMethodType<this>;
    get: _SessionRouteMethodType<this>;
    post: _SessionRouteMethodType<this>;
    put: _SessionRouteMethodType<this>;
    head: _SessionRouteMethodType<this>;
    delete: _SessionRouteMethodType<this>;
    use: _SessionRouterMethodType<this>;
}

declare class _SessionApplication extends _SessionRouter
{
    // adapted from express$Application
    constructor(): void;
    locals: {[name: string]: mixed};
    mountpath: string;
    listen(port: number, hostname?: string, backlog?: number, callback?: _SessionNext): Server;
    listen(port: number, hostname?: string, callback?: _SessionNext): Server;
    listen(port: number, callback?: _SessionNext): Server;
    listen(path: string, callback?: _SessionNext): Server;
    listen(handle: Object, callback?: _SessionNext): Server;
    disable(name: string): void;
    disabled(name: string): boolean;
    enable(name: string): void;
    enabled(name: string): boolean;
    engine(name: string, callback: Function): void;
    // FlowType does not allow get() to be declared
    // get(name: string): mixed;
    set(name: string, value: mixed): mixed;
    render(name: string, optionsOrFunction: {[name: string]: mixed}, callback: express$RenderCallback): void;
}
*/

class RouterWrapper
{
    // largely calls router via apply(), so the type doesn't matter
    /*:: router: any; */
    
    constructor(options, router) {
        if (router)
            this.router = router;
        else
            this.router = express.Router(options);
    }
    
    use(/*arguments*/) {
        // Apps only call use() at initialization, so it's okay to have a
        // heavy implementation. Express requires its own router type.
        
        let args = Array.prototype.slice.call(arguments);
        for (let i = 0; i < args.length; ++i) {
            if (args[i] instanceof RouterWrapper)
                args[i] = args[i].router;
        }
        return this.router.use.apply(this.router, args);
    }
    
    get(/*arguments*/) {
        return this.router.get.apply(this.router, arguments);
    }
    
    post(/*arguments*/) {
        return this.router.post.apply(this.router, arguments);
    }
    
    put(/*arguments*/) {
        return this.router.put.apply(this.router, arguments);
    }
    
    delete(/*arguments*/) {
        return this.router.delete.apply(this.router, arguments);
    }
}

class AppWrapper extends RouterWrapper
{
    constructor() {
        super(null, express());
    }
    
    listen() {
        return this.router.listen.apply(this.router, arguments);
    }
        
    disable(name) {
        this.router.disable(name);
    }
    
    disabled(name) {
        return this.router.disabled(name);
    }
    
    enable(name) {
        this.router.enable(name);
    }
    
    enabled(name) {
        return this.router.enabled(name);
    }
    
    engine(name, callback) {
        this.router.engine(name, callback);
    }
    
    /*
    // TBD: add support for this additional signature. The base router uses
    // get() only as an HTTP method.
    get(name) {
        return this.router.get(name);
    }
    */
    
    set(name, value) {
        return this.router.set(name, value);
    }
    
    render(name, optionsOrFunction, callback) {
        this.router.render(name, optionsOrFunction, callback);
    }
}

//// EXPORTS //////////////////////////////////////////////////////////////////

/*::

// The $-prefixed variables correspond to their namesake Express objects.

export type $Application = _SessionApplication;
export type $Router = _SessionRouter;
export type $Request = _SessionRequest;
export type $Response = _SessionResponse;

// NextFunc defines the callback that Express "middleware" handlers use.

export type NextFunc = _SessionNext;

// StackHandler provides the usual signature for a "middleware" handler.

export type StackHandler =
        (req: $Request, res: $Response, next: NextFunc) => void;

// FormFields generically defines the value of req.body when populated by an HTML input field. Only use this when the input field names are dynamic. If the names can be hardcoded, instead define them as a specific type of object.

export type FormFields = { [key: string]: string };
*/

exports = module.exports = function () /*:$Application*/
{
    return ((new AppWrapper() /*:any*/) /*:$Application*/);
};

    
exports.static = function (root /*:string*/, options /*?:Object*/)
        /*:_SessionMiddleware*/
{
    return express.static(root, options);
}

exports.Router = function (options /*?:express$RouterOptions*/) /*:$Router*/
{
    return ((new RouterWrapper(options) /*:any*/) /*:$Router*/);
};
Read more comments on GitHub >

github_iconTop Results From Across the Web

Is there a type in @types/express for Express middleware ...
use('/foo', <MiddleWareFn> function(req,res,next){});. however I am wondering if Express has typings for middleware functions already? node.js ...
Read more >
Complete Guide to Express Middleware - Reflectoring
Middleware in Express are functions that come into play after the server receives the request and before the response is sent to the...
Read more >
How to write Express.js middleware with TypeScript
A “middleware” in Express is just a function that intercepts every request so that you can return a custom response or apply a...
Read more >
Middleware in Express.js - GeeksforGeeks
Middleware in Express.js · Middleware can process request objects multiple times before the server works for that request. · Middleware can be ...
Read more >
Build and Understand Express Middleware through Examples
Express middleware are functions that execute during the lifecycle of a request to the Express server. Each middleware has access to the HTTP ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found