109 lines
3.7 KiB
Markdown
109 lines
3.7 KiB
Markdown
# Writing and Using Middleware in HTTPure
|
|
|
|
Since HTTPure routers are just pure functions, you can write a middleware by
|
|
simply creating a function that takes a router and an `HTTPure.Request`, and
|
|
returns an `HTTPure.ResponseM`. You can then simply use function composition to
|
|
combine middlewares, and pass your router to your composed middleware to
|
|
generate the decorated router!
|
|
|
|
See [the Middleware example](./Examples/Middleware/Main.purs) to see how you can
|
|
build, compose, and consume different types of middleware.
|
|
|
|
## Writing Middleware
|
|
|
|
A middleware is a function with the signature:
|
|
|
|
```purescript
|
|
(HTTPure.Request -> HTTPure.ResponseM) -> HTTPure.Request -> HTTPure.ResponseM
|
|
```
|
|
|
|
Note that the first argument is just the signature for a router function. So
|
|
essentially, your middleware should take a router and return a new router.
|
|
That's it! You can do pretty much anything with middlewares. Here are a few
|
|
examples of common middleware patterns:
|
|
|
|
You can write a middleware that wraps all future work in some behavior, like
|
|
logging or timing:
|
|
|
|
```purescript
|
|
myMiddleware router request = do
|
|
doSomethingBefore
|
|
response <- router request
|
|
doSomethingAfter
|
|
pure response
|
|
```
|
|
|
|
Or perhaps a middleware that injects something into the response:
|
|
|
|
```purescript
|
|
myMiddleware router request = do
|
|
response <- router request
|
|
HTTPure.response' response.status response.headers $
|
|
response.body <> "\n\nGenerated using my super duper middleware!"
|
|
```
|
|
|
|
You could even write a middleware that handles routing for some specific cases:
|
|
|
|
```purescript
|
|
myMiddleware _ { path: [ "somepath" ] } = HTTPure.ok "Handled by my middleware!"
|
|
myMiddleware router request = router request
|
|
```
|
|
|
|
Or even a middleware that conditionally includes another middleware:
|
|
|
|
```purescript
|
|
myMiddleware router = if something then someOtherMiddleware router else router
|
|
```
|
|
|
|
Just make sure your middlewares follow the correct signature, and users will be
|
|
able to compose them at will!
|
|
|
|
Note that because there is nothing fancy happening here, you could always write
|
|
higher order functions that don't follow this signature, if it makes sense. For
|
|
instance, you could write a function that takes two routers, and selects which
|
|
one to use based on some criteria. There is nothing wrong with this, but you
|
|
should try to use the middleware signature mentioned above as much as possible
|
|
as it will make your middleware easier to consume and compose.
|
|
|
|
## Consuming Middleware
|
|
|
|
Consuming middleware easy: simply compose all the middleware you want, and then
|
|
pass your router to the composed middleware. For instance:
|
|
|
|
```purescript
|
|
main = HTTPure.serve port composedRouter $ Console.log "Server is up!"
|
|
where
|
|
composedRouter = middlewareA <<< middlewareB <<< middlewareC $ router
|
|
```
|
|
|
|
Be aware of the ordering of the middleware that you compose--since we used
|
|
`<<<`, the middlewares will compose right-to-left. But because middlewares
|
|
choose when to apply the router to the request, this is a bit like wrapping the
|
|
router in each successive middleware from right to left. So when the router
|
|
executes on a request, those middlewares will actually _execute_
|
|
left-to-right--or from the outermost wrapper inwards.
|
|
|
|
In other words, say you have the following HTTPure server:
|
|
|
|
```purescript
|
|
middleware letter router request = do
|
|
EffectClass.liftEffect $ Console.log $ "Starting Middleware " <> letter
|
|
response <- router request
|
|
EffectClass.liftEffect $ Console.log $ "Ending Middleware " <> letter
|
|
pure response
|
|
|
|
main = HTTPure.serve port composedRouter $ Console.log "Server is up!"
|
|
where
|
|
composedRouter = middleware "A" <<< middleware "B" $ router
|
|
```
|
|
|
|
When this HTTPure server receives a request, the logs will include:
|
|
|
|
```
|
|
Starting Middleware A
|
|
Starting Middleware B
|
|
...
|
|
Ending Middleware B
|
|
Ending Middleware A
|
|
```
|