Skip to content

Instantly share code, notes, and snippets.

@mrmurphy
Created August 7, 2018 17:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrmurphy/607d9f41a7f121229ac8690fd1f705e0 to your computer and use it in GitHub Desktop.
Save mrmurphy/607d9f41a7f121229ac8690fd1f705e0 to your computer and use it in GitHub Desktop.
Type Safe Reason Express Handlers
/* Prom is a library that provides
<$> (map)
and
>>= (bind, or flatMap)
for promises.
*/
open Prom;
/*
A Handler is a more functional and type-safe way of dealing with composable "middleware" in the express chain.
This lets the user chain handlers that can either respond to the request with their own response (in the case
of something like an unauthorized request) or pass along some new information to the next function in line.
The types are structured such that a chain must always be ended before it can be used by express. Thus
providing a good degree of type safety around the middleware experience with, hopefully, minimum complexity.
Should be used something like this:
startWith(thing that converts a request to some data or response)
|. andThen(some other thing that adds more data or responds)
|. andThen(some other thing that adds more data or responds)
|. endWith(some thing that turns that data into a response)
*/
module Status = Express.Response.StatusCode;
type intermediateResult('a) =
| Respond(Express.Response.StatusCode.t, Js.Json.t)
| Continue('a);
type middle('a, 'b) =
(Express.Request.t, 'a) => Js.Promise.t(intermediateResult('b));
type endResult = (Status.t, Js.Json.t);
type end_('a) = (Express.Request.t, 'a) => Js.Promise.t(endResult);
let startWith =
(fn: Express.Request.t => Js.Promise.t(intermediateResult('a)))
: middle(unit, 'a) =>
(req, _) => fn(req);
let andThen =
(source: middle('a, 'b), next: middle('b, 'c))
: middle('a, 'b) =>
(req, a) =>
source(req, a)
>>= (
resB =>
switch (resB) {
| Respond(code, content) =>
Js.Promise.resolve(Respond(code, content))
| Continue(b) => next(req, b)
}
);
let endWith =
(middleware: middle(unit, 'b), end_: end_('b))
: Express.Middleware.t =>
Express.PromiseMiddleware.from((_next, req, res) =>
middleware(req, ())
>>= (
resB =>
switch (resB) {
| Continue(b) =>
end_(req, b)
<$> (
((status, content)) =>
res
|> Express.Response.status(status)
|> Express.Response.sendJson(content)
)
| Respond(status, content) =>
Js.Promise.resolve(
res
|> Express.Response.status(status)
|> Express.Response.sendJson(content),
)
}
)
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment