purescript-httpurple/docs/Middleware.md
2018-07-08 16:28:59 -07:00

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
```