Bubblegum — a nix CGI programming framework

Nix may not be a general purpose programming language, but it is a pretty decent special purpose programming language. It is pretty good at describing managing sets of package descriptions and — as it turns out — CGI programming. To be precise the following properties make it pretty feasible:

  • builtins.getEnv allows us very easy access to the environment which is the main way the web server communicates with a CGI application
  • The powerful string interpolation Nix offers makes using a templating engine practically obsolete and rendering HTML not feel like a chore.
  • Attribute set can be used to make routing logic very simple and comprehensible, both in expression and implementation. For example: route = path: routes."${path}" or notFound.

With bubblegum a simple CGI application looks like this:

{ depot, ... }:

let
  inherit (depot.web.bubblegum)
    respond
    ;
in

respond "OK" {
  "Content-type" = "text/html";
  # further headers…
} ''
  <!doctype html>
  <html>
    <head>
      <meta charset="utf-8">
      <title>hello world</title>
    </head>
    <body>
      hello world!
    </body>
  </html>
''

Of course applications are somewhat limited due to the nature of nix:

  • It is pretty slow: Currently it uses a simple wrapper around nix-instantiate to interpret CGI scripts which already takes between 200ms and 300ms for trivial scripts and well over 1.5s for scripts using import from derivation where the output is already present in /nix/store.
  • We can’t cause any effects except for adding files and directories to the nix store, building derivations and performing GET requests using builtins.fetchurl (although this probably only works with tarball TTL 0 and would be very slow).
  • Error handling is very limited as builtins.tryEval doesn’t catch all errors nix evaluation generates.

However even in spite of that bubblegum can be used to write useful CGI applications. If you want to see for yourself you can run a few example scripts:

> git clone https://code.tvl.fyi/ depot && cd depot
> nix-build -A web.bubblegum && ./result
# navigate to http://localhost:9000

This distribution of examples contains a working file based blog and a SVG which is built on demand from a derivation.

Future improvements would be supporting request bodies and thus POST and the introduction of streaming (maybe even multiplexed) I/O using a custom interpreter and emulated lazy lists via attribute sets.

Full source code can be browsed in cgit here.

21 Likes

lol

I really love this.

4 Likes

The more you dig into this, the better it gets.

nint is a shebang compatible interpreter for nix. It is currently
implemented as a fairly trivial wrapper around nix-instantiate --eval.
It allows to run nix expressions as command line tools if they conform
to the following calling convention

<3

This is amazing! Massive kudos.

3 Likes

This is so insane and impractical that it’s hilarious, in every positive way. Great stuff!

5 Likes