When a client connects to this Web app, it sends back one paragraph per second in a response stream:
let render = response => {
%% response
<html>
<body>
% let rec paragraphs = index => {
<p><%i index %></p>
% let%lwt () = Dream.flush(response);
% let%lwt () = Lwt_unix.sleep(1.);
% paragraphs(index + 1);
% };
% let%lwt () = paragraphs(0);
</body>
</html>
};
let () =
Dream.run
@@ Dream.logger
@@ _ => Dream.stream(~headers=[("Content-Type", Dream.text_html)], render);$ cd example/r-template-stream
$ npm install esy && npx esy
$ npx esy startVisit http://localhost:8080 to see it in action.
The important differences with regular usage of templates are:
- We create the response with
Dream.stream, which is a convenience wrapper around some lower-level functions that would prepare a response for streaming. - We use the opening line
%% responseto tell the templater that we don't want to build a string, but to stream the template to a response in scope under the nameresponse. - We use the promise library Lwt inside the
template for asynchronous control flow. See example
5-promisefor an introduction to Lwt.
The call to Dream.flush isn't
necessary in most real-world cases — Dream's HTTP layer automatically
schedules the writing of data. However, this example is trying to appear
interactive, so we force writing of all output after generating each <p> tag.
See also:
r-templatefor the simpler way to do templates, building up entire bodies as strings.7-templatesection Security for XSS prevention considerations.w-template-streamis the OCaml version of this example.