DEV Community

Soichi Takamura
Soichi Takamura

Posted on • Updated on

Stop reinventing server middlewares and take advantage of Express ecosystem

TL;DR

flow-middleware creates a portable server handler from any of Express middlewares. You can run all the adapters of express-session and Passport.js stategies in your Next.js, micro and Node native server today.

import flow from 'flow-middleware';

const handle = flow(
    // Pass Express middlewares
    cookieParser(),
    session({ secret: 'x' }),
    flash(),
    (_reqProxy, resProxy, next) => {
        resProxy.redirect('/');
    }
);

// Handles Node native IncomingMessage and ServerResponse
handle(req, res);

Checkout the Next.js example with Passport.js integration.

Why

As people start using a new Node server library other than Express, they encounter a lack of middlewares that Express already has, which has been well tested and production-ready many years ago. Some of them try to shape a brand new ecosystem on the new island and some just sail back to Express.

Let's start from admitting Express is one of the most successful, beautifully designed and battle-tested software in the Node-based software. Don't forget its hundreds of outstanding middlewares have been born upon it.

Then why you can't use them? The answers will be summarized into these two:

  • It just can't run since they depend on Express's req.param() and res.redirect(). I don't want to hack to make them work in my ${Your favorite server name comes here}.
  • Pollution. Express officially recommends middlewares to extend object properties such as req.session = {} and req.flash = fn, where my ${Your favorite server} leaves them clean. Plus, dynamic extensions don't fit today of the TypeScript era.

Yeah, yeah. Then how to solve them both?

How

JavaScript Proxy.

Wrapping req and res by Proxy that splits Node native and Express properties to be accessed. Express exports clean prototypes we can intercept middleware's calls with. It also lets middlewares to call native methods like res.writeHead() and res.end() so native objects properly embed HTTP info like headers and send the response back.

The handler, in the end, returns the proxy objects with like req.session and req.user so you can still use them after the middlewares go through.

Getting started

Install it with Express.

yarn add flow-middleware express

flow(...middlewares)

A function flow creates an http handler from some Express middlewares, processed from left to right of arguments.

import flow from 'flow-middleware';
import { ok } from "assert";
import { createServer } from 'http';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import flash from 'express-flash';

// Creates an async function that handles req and res.
const handle = flow(
    cookieParser(),
    session({ secret: 'x' }),
    flash(),
    (reqProxy, _resProxy, next) => {

        // Our wrapped objects provide accessors
        // that Express middlewares extended💪
        ok(reqProxy.cookies);
        ok(reqProxy.session);
        ok(reqProxy.flash);
        next();
    }
);

createServer(async (req, res) => {

    // Let's run the Express middlewares🚀
    const [ reqProxy, resProxy ] = await handle(req, res);

    // Native objects are clean thanks to our proxy✨
    ok(req.cookies === undefined);
    ok(req.session === undefined);
    ok(req.flash === undefined);

    // You still can access to Express properties here🚚
    ok(reqProxy.cookies);
    ok(reqProxy.session);
    ok(reqProxy.flash);
    ok(resProxy.cookie);
    ok(resProxy.redirect);

    res.end('Hello!');
}).listen(3000);

Check out compose() function to flexibly share middlewares between multiple endpoints.

Conclusion

It takes years for a new framework to have bug-free middlewares for all the sessions, OAuth and logger adapters. Why don't you take advantage of Express ecosystem, one of the most stable solutions for Node.js?

Top comments (0)