Skip to content

Commit

Permalink
working on middlewares docs
Browse files Browse the repository at this point in the history
  • Loading branch information
shawn-mcginty committed Apr 21, 2020
1 parent 96e0682 commit ecbeb94
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ naboris.install
esy.lock
command-env
libev.esy.lock
.DS_Store
101 changes: 98 additions & 3 deletions docs-src/content/docs/guides/middlewares.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,101 @@ title: Middlewares
A set of functions that either respond to an http request or pass it on to the next
middleware on the stack.

- [Writing Middlewares](#writing-middlewares)
- [Using Middlewares](#using-middlewares)
- [Built In Middlewares](#built-in-middlewares)
For this guide we will be writing two example middlewares. One to log every request and how long it takes and another which will guard a section of the website exclusive to admin users.

- [Basics](#basics)
- [Authorization Example](#authorization-example)
- [Logger Example](#logger-example)
- [Combining Middlewares](#combining-middlewares)
- [Built In Middlewares](#built-in-middlewares)

#### <a name="basics" href="#basics">#</a> Basics
Middlewares have a wide variety of uses. They are executed __in the order in which they are registered__ so be sure to keep that in mind. Check out the [`Middleware module`](/odocs/naboris/Naboris/Middleware/index.html) for the spec:

```reason
// Middleware module
type t('sessionData) = (RequestHandler.t('sessionData), Route.t, Req.t('sessionData), Res.t) => Lwt.t(Res.t)
```
```ocaml
(* Middleware module *)
type 'sessionData t = 'sessionData RequestHandler.t -> Route.t -> 'sessionData Req.t -> Res.t -> Res.t Lwt.t
```

Middlewares can either handle the http request/response lifecycle themselves or call the passed in request handler (which is the next middleware in the stack) passing the `route`, `req`, and `res`. Once the list of middlewares has been exhausted it will then be passed on to the main request handler.

#### <a name="authorization-example" href="#authorization-example">#</a> Authorization Example
We can easily create a middleware function to protect certain endpoints of our application. All middlware functions will be executed before the `requestHandler`, making it easy to stop the request/response lifecycle if needed.

```reason
// ...
let serverConf: Naboris.ServerConfig.t(userData) = Naboris.ServerConfig.create()
|> Naboris.ServerConfig.addMiddleware((next, route, req, res) => switch (Naboris.Route.path(route)) {
| ["admin", ..._] => switch (Naboris.Req.getSessionData(req)) {
| Some({ is_admin: true, ..._}) => next(route, req, res)
| _ =>
res
|> Naboris.Res.status(401)
|> Naboris.Res.text(req, "Unauthorized");
}
| _ => next(route, req, res)
});
// ...
```
```ocaml
(* ... *)
let server_conf: user_data Naboris.ServerConfig.t = Naboris.ServerConfig.create ()
|> Naboris.ServerConfig.addMiddleware (fun next route req res ->
match (Naboris.Route.path route) with
| "admin" :: _ ->
(match (Naboris.Req.getSessionData req) with
| Some({ is_admin = true; _}) -> next route req res
| _ ->
res
|> Naboris.Res.status 401
|> Naboris.Res.text req "Unauthorized")
| _ -> next route req res)
(* ... *)
```

The above middleware example will check the session data for an `is_admin` flag on any route starting with `/admin/`, if the flag is present `next` is called and the request is handled as normal. If the flag is _not_ present the middleware completes the request/response lifecycle by sending a `401 - Unauthorized` response.


#### <a name="logger-example" href="#logger-example">#</a> Logger Example
Middlewares can also execute code _after_ the `next` function is called. This will be done in the next example which will log all incoming requests and how long it took for them to finish.

```reason
// ...
let serverConf: Naboris.ServerConfig.t(userData) = Naboris.ServerConfig.create()
|> Naboris.ServerConfig.addMiddleware((next, route, req, res) => {
let startTime = Unix.gettimeofday();
let path = String.concat("", Naboris.Route.path(route)) ++ Naboris.Route.rawQuery(route);
print_endline("Start Serving - " ++ path);
Lwt.bind(() => next(route, req, res), (servedResponse) => {
// this code is executed after next() resolves
let endTime = Unix.gettimeofday();
let ms = (endTime -. startTime) *. 1000);
print_endline(path ++ " - " ++ string_of_int(Res.status(servedResponse)) ++ " - in " ++ string_of_float(ms) ++ "ms");
Lwt.return(servedResponse);
});
});
// ...
```
```ocaml
(* ... *)
let server_conf: user_data Naboris.ServerConfig.t = Naboris.ServerConfig.create ()
|> Naboris.ServerConfig.addMiddleware (fun next route req res ->
let start_time = Unix.gettimeofday () in
let path = (String.concat "" (Naboris.Route.path route)) ^ (Naboris.Route.rawQuery route) in
let _ = print_endline ("Start Serving - " ^ path) in
Lwt.bind(fun () -> next(route, req, res), fun servedResponse ->
(* this code is executed after next() resolves *)
let end_time = Unix.gettimeofday () in
let ms = (end_time -. start_time) *. 1000 in
let _ = print_endline (path ^ " - " ^ (string_of_int (Res.status servedResponse)) ^ " - in " ^ string_of_float(ms) ^ "ms") in
Lwt.return servedResponse))
(* ... *)
```

#### <a name="combining-middlewares" href="#combining-middlewares">#</a> Combining Middlewares

#### <a name="built-in-middlewares" href="#built-in-middlewares">#</a> Built In Middlewares
2 changes: 2 additions & 0 deletions docs/html/naboris/Naboris/Middleware/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Middleware (naboris.Naboris.Middleware)</title><link rel="stylesheet" href="../../../odoc.css"/><meta charset="utf-8"/><meta name="generator" content="odoc 1.5.0"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../../../highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div class="content"><header><nav><a href="../index.html">Up</a><a href="../../index.html">naboris</a> &#x00BB; <a href="../index.html">Naboris</a> &#x00BB; Middleware</nav><h1>Module <code>Naboris.Middleware</code></h1><p>Module defining middleware functions.</p></header><aside></aside><dl><dt class="spec type" id="type-t"><a href="#type-t" class="anchor"></a><code><span class="keyword">type</span> <span>'sessionData t</span></code><code> = <span><span class="type-var">'sessionData</span> <a href="../RequestHandler/index.html#type-t">RequestHandler.t</a></span> <span>&#45;&gt;</span> <a href="../Route/index.html#type-t">Route.t</a> <span>&#45;&gt;</span> <span><span class="type-var">'sessionData</span> <a href="../Req/index.html#type-t">Req.t</a></span> <span>&#45;&gt;</span> <a href="../Res/index.html#type-t">Res.t</a> <span>&#45;&gt;</span> <span><a href="../Res/index.html#type-t">Res.t</a> Lwt.t</span></code></dt></dl></div></body></html>
2 changes: 2 additions & 0 deletions docs/html/naboris/Naboris/RequestHandler/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>RequestHandler (naboris.Naboris.RequestHandler)</title><link rel="stylesheet" href="../../../odoc.css"/><meta charset="utf-8"/><meta name="generator" content="odoc 1.5.0"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../../../highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div class="content"><header><nav><a href="../index.html">Up</a><a href="../../index.html">naboris</a> &#x00BB; <a href="../index.html">Naboris</a> &#x00BB; RequestHandler</nav><h1>Module <code>Naboris.RequestHandler</code></h1><p>Module defining RequestHandler functions.</p></header><aside></aside><dl><dt class="spec type" id="type-t"><a href="#type-t" class="anchor"></a><code><span class="keyword">type</span> <span>'sessionData t</span></code><code> = <a href="../Route/index.html#type-t">Route.t</a> <span>&#45;&gt;</span> <span><span class="type-var">'sessionData</span> <a href="../Req/index.html#type-t">Req.t</a></span> <span>&#45;&gt;</span> <a href="../Res/index.html#type-t">Res.t</a> <span>&#45;&gt;</span> <span><a href="../Res/index.html#type-t">Res.t</a> Lwt.t</span></code></dt></dl></div></body></html>
2 changes: 1 addition & 1 deletion docs/html/naboris/Naboris/ServerConfig/index.html

Large diffs are not rendered by default.

Loading

0 comments on commit ecbeb94

Please sign in to comment.