Fast HTML templates for Racket servlets with include-template
Templates are the bread and butter of web development. As with many things in Racket, there's more than one way of doing things, and it's not always easy to keep all of them straight. I myself use a mixture of approaches, depending on my needs. In another tutorial, I talked a bit about about txexpr. Here's a brief description of another approach to HTML generation,
include-template, that has some nice advantages and is definitely worth knowing about.
include-template takes a file and makes a function that, given some variables as arguments, produces a string. It's fast and simple. Let's cut straight to an example:
<!doctype html> <html lang="en"> <head> <title>Rational numbers are awesome</title> </head> <body> <p>Today I learned that the reciprocal of @|n| is @|r|.</p> </body> </html>
Store that in a file, say,
reciprocal.html and include it in your web program with
(Don't forget to include
in your code. That's where
include-template comes from.)
r show up in the template, here using the Scribble
@ notation (and, moreover, surrounded by pipes
|) are Racket identifiers. They should be in lexical scope wherever you do
include-template. Here's a complete example:
(let* ([n 5] [r (/ 1 n)]) (include-template "reciprocal.html"))
The values for
r are hardcoded here, but could come from anywhere. (Perhaps data coming from an HTTP POST request?)
Some noteworthy features of the Scribble-based approach:
Direct: There's no
#lang scribbleat the top. There's no
#langat all. You cut straight away to your markup. Thus, although the
@notation comes from Scribble, these templates are not Scribble documents.
Lexical scoping There's no chance that you use a variable in your template that is not in lexical scope where the template is included. You can't even start your web app.
Fast The templates are included into your program at compile-time; the file is thus loaded only as many times as it's mentioned in your program. Once your server is running, the file won't be consulted again.
(This performance tuning is not without some disadvantages, though. If you've got big templates, or lots of them, they may make your program, well, a bit large.)
include-template allows you to do is define functions whose value is a string.
include-template does not generate a whole HTTP response, and using it doesn't close an HTTP connection. In fact,
include-template is just syntax; it gets compiled away, leaving your with a function that generates a string. It's your job to take the result of
include-template and generate an HTTP response.