Over at #nim (on irc/freenode) I often see the following question from users both new and old; "How do I create a web application with Nim?" so I thought I'd take a crack at putting to rest what I view as a fairly solved problem that there doesn't appear to be a lot of information about.
Firstly, the technologies we'll use:
The top four, I think, are at the core of what you will need, and are more or less required - at least to follow this guide. The bottom two are optional, but I highly recommend them! If you already have your Nim environment all set up, please feel free to skip to the next section.
If you don't already have Nim installed, I always recommend using git head from here. Next you'll need to clone Nimble. Nimble is Nim's package manager, allowing us to grab some more tools we'll need later on. Instructions for installing both of the aforementioned projects can be found in their respective READMEs.
Jester is a basic DSL (domain-specific language) for handling HTTP requests, interpreting their paths and query strings and yielding back an HTTP response. Once Nimble is set up, you can install it by running 'nimble install jester'
The Nim-Templates library is already discribed in this post, but briefly; it allows you to transform well-structured strings (in this case HTML) into nim code at compile-time; Turning something like this:
tmpl html"""
<div>$myVar</div>
"""
into something like this:
result.add("<div>")
result.add(myVar)
result.add("</div>")
You can install it by running 'nimble install templates'
Nim-Lime is a Sublime Text 2/3 plugin. As I mentioned earlier, this component isn't strictly necessary, but it does improve the usage of Nim-Templates quite drastically with its support for sub-syntax highlighting. If you're new to Nim, and aren't already comfortable with your development setup, I highly recommend using this plugin; I generally just 'git clone' the nimlime repo into the Sublime Text Packages directory.
I'm not going to delve too deeply into any philosophy here, so I'll just get down to it; the skeleton of a web application. The example is a basic 'TODO' application, which allows you to view, add, and remove 'TODO' items from a list.
At the core of the app is the 'routes' section, which tells jester how you want to handle requests.
# todo.nim
routes:
get "/": resp views.index()
get "/add": resp views.index()
post "/add":
add(@"newItem")
resp views.index()
get "/remove/@id":
remove(@"id")
resp views.index()
The 'resp' procedure expects a string, which the 'index' procedure provides. The '@' symbol in the routes is used to define (and retrieve) both querystring and form values. The index procedure, defined in the 'views.nim' file, renders the content of the default page using a couple basic HTML nim-templates.
proc index*: string =
# If there are TODO items, render them
if todoItems.len > 0:
tmpli html"""
<ul>
$for item in todoItems {
<li>$item</li>
}
</ul>
"""
# Otherwise, render a message saying there is nothing to do
else:
tmpli html"""
<div id="warning">No TODO items</div>
"""
# Display a small form at the bottom allowing the user
# to add a new TODO item.
tmpl html"""
<form action="/add" method="post">
<label for="newItem">New Item:</label>
<input id="newItem" name="newItem">
<button name=>Add</button>
</form>
"""
return master.layout("TODO", result)
At the bottom we pass the resulting string into the 'layout' procedure, which wraps it with our standard 'master page' which we define in the master.nim file. This pattern is obviously quite useful if you have a lot of HTML surrounding your content that you need to repeat often.
Anything in the 'public' directory will automatically be hosted statically by Jester, in this case we have our stylesheet, but this would likely be used to serve up your javascript and images as well.
Beyond a bit of boiler plate code like imports, the last thing you'll typically want to do in your app is tell the async dispatcher to keep running (so the program doesn't immediately exit) by calling 'runForever()'
You can browse through the full source code here:
I definitely do not recommend exposing jester directly to the internet. You will want hide behind a powerful web server like nginx. It's fast, simple, and secure. You can do this by configuring nginx to do a "proxy_pass" to port 5000 (Jester's default port, which you can change if need-be), which will forward web requests to your app.
I also recommend configuring nginx to handle your 'public' routes so that jester doesn't need to do the work for simple static files.
If you have any more questions, feel free to log onto IRC (#nim / freenode) and ask away.