Separation of Handlers from Middleware, and next
delegate from Context
#7
Replies: 3 comments 6 replies
-
I do agree separating function cookieParser(req, ctx) {
ctx.cookie = cookie.parse(req.headers.get("cookie") || "");
} (This works because returning vs function cookieParser(next) {
return (req, ctx) => {
ctx.cookie = cookie.parse(req.headers.get("cookie") || "");
return next();
}
} In my opinion, the first one is:
I'm not particularly concerned about blurring the distinction between handlers and middleware. "Call each handler until one returns a response" is a very simple mental model.
The use case is important for adoption: It allows people who can't live without a specific Express middleware to adopt HatTip even before that functionality becomes available natively. It's also important because I'm basing the next version of my Vite-based React framework Rakkas on HatTip and Express interoperability is a frequently requested feature. (HatTip integration is happening on the The "last resort" error handler and By the way the readme is a bit out of date: User code rarely needs What particular aspect of the second version do you think is better compared to the first version? |
Beta Was this translation helpful? Give feedback.
-
Here's an excerpt of something I wrote during one of our chats with @brillout while discussing the basic API:
I guess I was getting old too :D Aside: We had decided to go with 1b like in the current code but some experimentation with the API prompted me to reconsider and we rediscussed to settle on 1a. Not yet implemented though. You're right that a cookie parser doesn't need to return a response. But an authentication guard does, for instance. And an Like I said, I believe middleware is not a good pattern and we shouldn't organize our apps in terms of them. But for providing and consuming lower level services (like parsing cookies or adding CORS headers), they are easy to implement, easy to use, and easy to grasp. Tried and true as well: We can bash Express all day long and it will still be everyone's tool of choice for many years to come thanks to its simplicity, conciseness, and pragmatism. (Until hattip catches on, that is :P ) |
Beta Was this translation helpful? Give feedback.
-
Also, while JavaScript does have the expressive power of doing things in a more functional way, most standard APIs don't. Consider a URL parser, just We can either fight the standard APIs and implement our own immutable URL object or embrace them and call such a middleware a "URL rewriter" :) |
Beta Was this translation helpful? Give feedback.
-
Just a thought: your
Handler
is actually what would be calledMiddleware
in other frameworks.Broadly, a "handler" is a
Request => Response
function, and "middleware" is aHandler => Handler
function that performs a simple functional composition.So I would like to suggest a formal definition of an actual
Handler
as something separate (and lower level) fromMiddleware
, as most frameworks generally have.Basically:
next
fromContext
.next
delegate part of a formalMiddleware
interface definition:This way, a
Middleware
is just a factory for aHandler
- and now you have aHandler
that is guaranteed to return, can't delegate to anynext
handler.The distinction is useful because your 404 handler, and controllers in general, cannot and should not delegate - these are examples of handlers that must return a
Response
.This removes the need for errors/exceptions when running middleware - and you get a
runHandler
that better reflects the reality that you can't actually have a middleware-stack without a final-handler... so, something like:This means you can get rid of the default 404 handler that's built-in - or at least make it plainly visible that one is required.
This probably removes the need for
isNotFound
inContext
and some conditions and relying onconsole.error
incompose.ts
.On the adapter side, this tends to simplify things a bit, since a
Handler
is more or less plainly callable.Middleware
becomes more of a userland concept - an implementation detail, as far as how users choose to create and compose handlers, while the framework itself can internally rely mostly onHandler
, which would simplify some things.Thoughts? 🙂
Beta Was this translation helpful? Give feedback.
All reactions