Middleware
Middleware helps with code reuse and orchestrating more sophisticated request pipelines. Webroute offers a simple, powerful and novel approach to middleware to avoid some of the pitfalls of previous approaches.
Overview
Webroute's approach to middleware is highly functional. Instead of middleware imperatively orchestrating the app, it is a pure function which merely accepts inputs and returns outputs. Consequently, many of the common bugs are avoided.
This approach means middleware are always strongly type-safe and connected in-code. Our routes will alway know what middleware runs before it and what state middleware may or may not provide.
Getting Started
To register a middleware function, we use the .use()
method. The handler we provide takes the same parameters as a request handler.
Middleware works incredibly well with the composition pattern.
Middleware Effects
Most middleware wants to change or orchestrate our app somehow. Because web-standard Request
s are immutable, we can't directly modify the Request
object. Moreover, webroute doesn't provide any function like next()
to orchestrate the app flow. Instead, webroute uses return values to enable these actions.
Updating Request State
A common use-case of middleware is adding some information to our request. In other words, we are storing some state about the current request. We can return a plain JavaScript object to indicate a state update is in order. Webroute will read the type of the object, which will be reflected in future access of the state
property.
State updates from additional middleware will be shallow merged with the request state.
Early Response
Middleware is commonly used to guard subsequent handlers by returning a response early under some condition. For example authentication and authorisation middleware.
If we return a Response
from middleware, this will trigger an early response.
Response Handlers
Another use for middleware is to listen to and potentially alter the Response
on its way out. Such response handlers are the third and final variant a middleware can return. Response handlers are functions which accept a Response
as the only parameter. They may optionally return an updated Response
.
Request middleware run in order of registration. Response handlers run in the reverse order.
Escape Hatch: Mutating State
Most of the time, middleware will only need to do one of these three things at once. They are almost mutually exclusive options. However, in rare cases we may want to update request state and return early or handle responses.
In this case, we can use the escape hatch of mutating state directly, i.e. without returning. Not only can we mutate state, but we can do so without sacrificing type-safety.
Here we provide a generic type parameter indicating how our state
s type will change.
Defining Middleware
While creating middleware inline is a valid approach, it makes reusing middleware more difficult. Instead, we can define middleware elsewhere.
As we've seen, middleware is inherently basic, so we can define middleware like so.
Instead of making our middleware reliant on the context param, we encourage specifically defining only what is needed. This helps with testing and will make your code less fragile.
A Realistic Example
So far the examples have been relatively contrived. Here is a more realistic glimpse at what working with middleware might look like.