Why, How
8 |
9 |
10 | # Why
11 |
12 | As people start using a new Node server library other than [Express](https://expressjs.com/), they encounter a lack of middlewares that Express already has, which have 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 go back to Express.
13 |
14 | Let's start from admitting Express is one of the most successful, beautifully designed and battle-tested software in the Node ecosystem. Don't forget its **hundreds of outstanding middlewares** have been born on it. Then why you can't use them? The answers will be summarized:
15 |
16 | * It breaks since they depend on `req.param()` and `res.redirect()` that Express decorates native objects with. I don't want to hack to make them work in my _${Your favorite server comes here}_.
17 | * Pollution. [Express officially recommends](https://expressjs.com/en/guide/writing-middleware.html) middlewares to extend object properties such as `req.session` and `req.flash`, just where my _${Your favorite server}_ leaves them tidy. Plus, dynamic extensions don't fit today of the TypeScript era.
18 |
19 | Yeah. Let's move on.
20 |
21 | # How
22 |
23 | JavaScript `Proxy`.
24 |
25 | Wrapping `req` and `res` by `Proxy` to split using native methods and Express methods. Express exports clean prototypes that we can intercept internal calls with. It lets middlewares to call native methods like `res.writeHead()` and `res.end()` so native objects properly embed HTTP info and send the response.
26 |
27 | In the end, flow-middleware returns the extended properties like `req.session` and `req.user` so you can use them after the middlewares go through.
28 |
29 |
30 |
31 |
32 | 
33 |
34 | # Getting started
35 |
36 | Install it with Express.
37 |
38 | ```bash
39 | yarn add flow-middleware express
40 | ```
41 |
42 | ### flow(...middlewares)
43 |
44 | A function `flow` creates an http handler from some Express middlewares, processed from left to right of arguments.
45 |
46 | ```typescript
47 | import flow from 'flow-middleware';
48 | import { ok } from "assert";
49 | import { createServer } from 'http';
50 | import cookieParser from 'cookie-parser';
51 | import session from 'express-session';
52 | import flash from 'express-flash';
53 |
54 | // Creates an async function that handles req and res.
55 | const handle = flow(
56 | cookieParser(),
57 | session({ secret: 'x' }),
58 | flash(),
59 | (reqProxy, _resProxy, next) => {
60 |
61 | // Our wrapped objects provide accessors
62 | // that Express middlewares extended💪
63 | ok(reqProxy.cookies);
64 | ok(reqProxy.session);
65 | ok(reqProxy.flash);
66 | next();
67 | }
68 | );
69 |
70 | createServer(async (req, res) => {
71 |
72 | // Let's run the Express middlewares🚀
73 | const [ reqProxy, resProxy ] = await handle(req, res);
74 |
75 | // Native objects are clean thanks to our proxy✨
76 | ok(req.cookies === undefined);
77 | ok(req.session === undefined);
78 | ok(req.flash === undefined);
79 |
80 | // You still can access to Express properties here🚚
81 | ok(reqProxy.cookies);
82 | ok(reqProxy.session);
83 | ok(reqProxy.flash);
84 | ok(resProxy.cookie);
85 | ok(resProxy.redirect);
86 |
87 | res.end('Hello!');
88 | }).listen(3000);
89 | ```
90 |
91 | ### compose(...middlewares)(...middlewares)()
92 |
93 | `compose` lets you hold a set of middlewares and share it on other routes. This is useful when you want the same initializing middlewares to come first while the different middlewares come at the end. **Calling it with zero arguments returns a handler function.**
94 |
95 | This is a Passport example where a login handler for `POST /api/auth/github` and an OAuth callback handler for `GET /api/auth/callback/github` share their initializing middlewares.
96 |
97 | ```typescript
98 | import cookieSession from 'cookie-session';
99 | import { compose } from 'flow-middleware';
100 | import passport from './passport';
101 |
102 | const composed = compose(
103 | cookieSession(),
104 | passport.initialize(),
105 | passport.session()
106 | );
107 |
108 | const handleToLogIn = composed(passport.authenticate('github'))();
109 |
110 | const handleForCallback = composed(passport.authenticate('github', {
111 | failureRedirect: '/auth',
112 | successRedirect: '/',
113 | }))();
114 | ```
115 |
116 | Don't forget to call it with zero arguments at last to get a handler.
117 |
118 | #### Wrapper function style
119 |
120 | Or, you can simply write a wrapper function to share middlewares.
121 |
122 | ```typescript
123 | import { Handler } from 'express';
124 |
125 | function withPassport(...middlewares: Handler[]) {
126 | return flow(
127 | cookieSession(),
128 | passport.initialize(),
129 | passport.session(),
130 | ...middlewares
131 | );
132 | }
133 | ```
134 |
135 | # License
136 |
137 | MIT
138 |
139 | # Author
140 |
141 | Soichi Takamura \