RSS Amplifier

My ramblings · Jan 4, 2026

How I’m writing web apps with Nim in 2026

0
Sign in to vote or save

This page cannot be shown here. You can still read it on the original site — the toolbar below keeps your place in the directory.

A few things have changed in the Nim webdev community since I last wrote in 2021 . The main update is that Jester is effectively in maintenance mode, and has some issues with the latest version of Nim. All of the other libraries/frameworks are still being actively developed (or are stable on the latest versions of Nim), which is a healthy sign. If you’re someone who would much rather browse…

A few things have changed in the Nim webdev community since I last wrote in 2021</a>. The main update is that Jester is effectively in maintenance mode, and has some issues with the latest version of Nim. All of the other libraries/frameworks are still being actively developed (or are stable on the latest versions of Nim), which is a healthy sign.</p>

If you’re someone who would much rather browse through code, I have a small sample app using this stack that can be found here</a>.</p>

Web server</a> </h2>

Since Jester is gone, I’ve switched to mummy</a>. It uses threads (instead of async/await), which leads to simpler code and even easier debugging due to stack traces. The author of mummy wrote a post on the Nim forum</a> that I recommend reading through on how he’s using it in production. It’s remarkably fast and stable - I’ve been running it on my web server uninterrupted for several months, and there have been absolutely no memory leaks or crashes in that time. I also used it to build sakura.arhamjain.com</a>, which saw traffic spikes during Sakura-Con (in Seattle), and never had any issues with scaling.</p>

import</span> mummy, mummy</span>/</span>routers</span></span>
</span>
proc</span> indexHandler</span>(request</span>:</span> Request</span>)</span> =</span>  </span></span>
  var</span> headers</span>:</span> HttpHeaders</span></span>
  headers[</span>"</span>Content-Type</span>"</span>] </span>=</span> "</span>text/plain</span>"</span>  </span></span>
  request</span>.</span>respond</span>(</span>200</span>, headers, </span>"</span>Hello, World\!</span>"</span>)</span></span>
</span>
var</span> router</span>:</span> Router</span>  </span></span>
router</span>.</span>get</span>(</span>"</span>/</span>"</span>, indexHandler)</span></span>
</span>
let</span> server </span>=</span> newServer</span>(router)  </span></span>
echo</span> "</span>Serving on http://localhost:8080</span>"</span>  </span></span>
server</span>.</span>serve</span>(</span>Port</span>(</span>8080</span>))</span></span></code></pre>
import</span> mummy, rody</span></span>
</span>
let</span> handler </span>=</span> route</span>:</span>  </span></span>
  headers[</span>"</span>Content-Type</span>"</span>] </span>=</span> "</span>text/html</span>"</span>  </span></span>
    at </span>"</span>/</span>"</span>:</span>   </span></span>
      get</span>:</span>  </span></span>
        resp “</span>hello </span>world”</span></span>
</span>
echo</span> "</span>Serving on http://localhost:8080</span>"</span>  </span></span>
newServer</span>(handler)</span>.</span>serve</span>(</span>Port</span>(</span>8080</span>))</span></span></code></pre>

ORM</a> </h2>

I’ve switched from Norm</a> to debby</a>. This isn’t because of any issues with Norm, it’s mostly because I like using libraries written by treeform and guzba - if a Nim library ends in “y”, there’s a 90% chance one of them wrote it. Debby is much more hands off compared to Norm - it can generate tables and query them, but it doesn’t handle joins or most complicated SQL syntax. Instead, it specializes in mapping the results of a SQL query to a Nim object in a typesafe way.</p>

import</span> debby</span>/</span>sqlite</span></span>
let</span> db </span>=</span> openDatabase</span>(</span>"</span>cars.db</span>"</span>)</span></span>
</span>
type</span> Car</span> =</span> ref</span> object</span>  </span></span>
  id</span>:</span> int</span>  </span></span>
  make</span>:</span> string</span>  </span></span>
  model</span>:</span> string</span>  </span></span>
  year</span>:</span> int</span></span>
</span>
db</span>.</span>createTable</span>(</span>Car</span>)</span></span>
</span>
var</span> car </span>=</span> Car</span>(</span></span>
  make</span>:</span> "</span>Chevrolet</span>"</span>,</span></span>
  model</span>:</span> "</span>Camaro Z28</span>"</span>,</span></span>
  year</span>:</span> 1970</span>  </span></span>
)</span></span>
db</span>.</span>insert</span>(car)</span></span>
car </span>=</span> db</span>.</span>get</span>(</span>Car</span>, car</span>.</span>id)</span></span>
car</span>.</span>year </span>=</span> 1971</span></span>
db</span>.</span>update</span>(car)</span></span>
db</span>.</span>delete</span>(car)</span></span></code></pre>

It handles the common cases succinctly, and avoids any magic, making it more difficult to accidentally end up with performance issues. It also means that you’re never waiting on an ORM to add support for some arcane feature or syntax - just write SQL directly. I’m also using SQLite for my apps, I don’t have nearly enough traffic to worry about scaling past that.</p>

UI</a> </h2>

I’ve switched from HTMX</a> to Unpoly</a>. HTMX is a fantastic library and I deeply respect the work that the authors have put into it. I used HTMX for several years and was pretty happy with it (when compared to traditional SPA frameworks and NPM). Both libraries are prioritizing HTML of the wire, but the key difference is that Unpoly is more “use-case” focused, while HTMX is more focused on “building-blocks”.</p>

For example, Unpoly’s doc will walk you through how to handle form validation, dependent form fields, and other common journeys. HTMX is much more hands off by comparison - they give you the building blocks (hx-get, hx-post) and leave it to the developer to decide how to handle form validation.</p>

Unpoly is more opinionated, but the opinions are designed for classic (2000-2010) web development. For example, Unpoly only issues GET and POST requests, making progressive enhancement (in case the user is not using JS) much easier to keep.</p>

Here’s a list of magical Unpoly features I’ve found useful that would require more thinking and potentially Javascript to replicate with HTMX:</p>

For routing, I wrote my own (very small) library,</a> inspired by Roda instead of using the router that Mummy ships with. I find that specifying a routing tree is more natural for resource based URL schemes, and it makes it easy to add hooks and middleware without introducing callbacks/registration.</p>

Read on /posts/nim-web-2026/

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.