“hypermedia systems”‘s Contact.app built up step by step

TL;DR

htmx can be quite useful, especially for python devs with not enough time to learn javascript frameworks but who needs an alternative to tools like streamlit.

The “hypermedia systems” book quite a good read, I recommend reading it.

My code discussed in this blog post very closely resembles the code in the “hypermedia systems” book, but is built up progressively, to make it easier to follow along the book when stepping through the chapters. I only cover parts I found essential though. But maybe my template is helpful to follow if you want to add components important to you.

Links to things

What is htmx?

It is a tool introduced in the book “hypermedia systems”. It’s a small javascript library you can install with little overhead, it extends HTML tags with attributes so you can use GET, POST, PUT and DELETE methods with various triggers, leading to new pages or replaces small pieces of the current one. There is also a small DELETE example below to illustrate the htmx capabilities.

The code in the “hypermedia systems” book

Starting to read the “hypermedia systems” book, I saw they provided code, hence I naturally wanted to implement it while reading along. Building something myself I find very helpful in actually understand it. But I quickly found that the code examples assumed presence of other, not discussed, code. Darn it! But I saw the authors referred to a github repo they have prepared, yay! Quickly jumping over there and cloning the repo I did find a working app, more yay! (how often do I find broken code …)

However, looking into the repo, trying to understand what I need to look at for the book in order to understand the mechanics, I found a bunch of javascript files, various html templates with htmx pieces I’ve not yet heard of in the book and I also couldn’t easily discern what code is necessary for the functionalities I’ve read about so far. This surprised me, I got the impression that the usage of htmx should be relatively easy, what is going on? Is the htmx business actually much more difficult than expected?

To find out I decided, instead of trying to understand the repo as is, to start from scratch, take a broom and clear out everything that is javascript and htmx and successively built it up, so plain file diffs can be used to understand the changes, limiting the added ideas to a minimum, following the chapters. Crossing fingers, hoping this decision will not lead me down unseen rabbit holes.

Turns out it in this case it was actually a sensible approach. The result of this ab intio journey is the main point of this blog post and can be found just after the following htmx teaser. 🙂

htmx teaser

To illustrate a very neat aspect of htmx let’s look at one code example to send a DELETE request, which is not supported by HTML for some reason, see the book for more :).

What htmx allows you to do is the following

<!-- apps/web2/templates/edit.html -->
<button 
  hx-delete="/contacts/{{ contact.id }}"
  hx-target="body">
    Delete Contact
</button>

In this example the button tag gets added a hx-delete method that will make the click event on the button send a DELETE HTTP request to /contacts/{{ contact.id }}. The curly braces part is jinja2 templating, essentially only contains some id. The hx-target defines what is to be replaced by the HTML that is returned from the backend. This can be different things, here it’s "body".

The backend, using flask, then looks something like

# apps/web2/app.py
@app.route("/contacts/<contact_id>", methods=["DELETE"])
def contacts_delete(contact_id=0):
    ...

So only a route needs to be specified with the method DELETE and that will call the function def contacts_delete. This function is the one that returns HTML that will replace the target "body".

Mapping of chapters to code / app versions

Building the “Contact.app” app in the book up from scratch resulted in five versions of the app.

The first is in the directory `apps/web1`. It contains the most basic version of Contact.app, only flask, html (with jinja2 templating) and the needed css.

apps/web2 is the first version of the app containing htmx. It introduced boosting of links & forms for efficiency, the usage of DELETE, the validation of input on client and server side and paging.

apps/web3 adds some htmx features for user convenience like active search, lazy loading and inline / bulk delete.

apps/web4 adds the management of a long running process, e.g. data download, really just to show that it is possible.

apps/web5 also demonstrates something for the sake of it :P, the addition of a json data api. So the state of the app can be changed, e.g. via curl requests, leading to changes on the client side / frontend.

The chapters / sections are mapped to the above folders as follows:

Final words

I hope this progressive build-up of the “Contact.app” from the book “hypermedia systems” is of help to you.

In the next blog post something with fasthtml may be coming up. What could it be? What could it possibly be? So hard to guess! Such uncertainty. 😀

So long and happy coding!

rust + nix home-manager + vscode

In a previous post I shared how I set up the nix home-manager on linux. Now I had the problem that I wanted to make the rust-analyzer work properly in VSCode. Yes, for some reason, just adding the packages to home.nix packages like

{ lib, pkgs, ... }:
{
  ...
  nix = {
    package = pkgs.nix;
    settings.experimental-features = [ "nix-command" "flakes" ];
  };
  home = {
    packages = with pkgs; [
      cargo
      rustc
      rustfmt
    ];
  };
}

as one may think reading the NixOS rust wiki page does not actually work. I mean, not that they have a home-manager flakes example, but that was my best guess based on what I’ve read there. So using this guess, you can successfully run home-manager switch ... and develop rust code, but then VSCode’s rust extension with rust-analyzer gives you a lovely

error[E0463]: can't find crate for `core`
  |
  = note: the `aarch64-apple-darwin` target may not be installed
  = help: consider downloading the target with `rustup target add aarch64-apple-darwin`
error[E0463]: can't find crate for `compiler_builtins`
error[E0463]: can't find crate for `core`

Which is not super helpful besides indicating it cannot find something it should. As a matter of fact it fails to find any rust standard / core source code, quite annoying to develop without hints based on that.

Well how to fix it?

It turns out you need to explicitly tell nix to set RUST_SRC_PATH, update your config and then restart VSCode. An abbreviated version of what I have found working is the following home.nix file

{ lib, pkgs, ... }:
{
  nix = {
    package = pkgs.nix;
    settings.experimental-features = [ "nix-command" "flakes" ];
  };
  home = {
    packages = with pkgs; [
      # rust: https://discourse.nixos.org/t/rust-src-not-found-and-other-misadventures-of-developing-rust-on-nixos/11570/8
      pkgs.latest.rustChannels.stable.rust
      pkgs.latest.rustChannels.stable.rust-src
    ];
    
    sessionVariables= {
      RUST_SRC_PATH="${pkgs.latest.rustChannels.stable.rust-src}/lib/rustlib/src/rust/library/"; # <---- important
    };
  # snip
  };
  # snip
}

The above probably also works with rust-bin or alternatives as well, I just stopped iterating.

Hope this helps!

nix home-manager is actually useful

This is for you if …

You have some spare time and want to dive into nix and or try another way to configure your machine environment.

And let me tell you, nix is a great way of remedying the problem of never having had all software admin problems at the same time!

Jokes aside, there actually is a decent way of managing your home setup, i.e. aliases and command line tools using home-manager.

Code

The results of me experimenting with nix and home-manager on an arch virtual machine can be found on github:

linux-nix-home-manager @ github

The arch virtual machine I’ve used I set up as described in the last two blog posts #1 and #2.

If you are contemplating modifying the flake.nix, home.nix or uv.nix files in the linked github repo, be aware that this may be quite a new type of dragon to ride, even if you have coding experience, and it may take some time to fend off all the errors. It took me some home-manager examples (why are so many of them so complicated?!), diving through forums, and reading up on the nix language to inch my config forward bit by bit. Maybe I’d been faster if I’d done these steps in a different order :P. But I’ve included helpful references in the github repo.

Why did I write this?

Having the config on my virtual machine was nice but then I wanted to use it on macOS and found that actually with some edits, which were only a minor pain compared to figuring out how to add uv :D, the setup worked es well! Also switching from bash on arch to zsh on macOS was super easy. Essentially I could reproduce my setup by 1) install nix, 2) clone repo, 3) minor edits in the nix files for OS specified entries and 4) build and done!

Also, if I want to get rid of a command line tool I just update my home-manager config and swoosh it’s gone, no need to worry which things may need removing anymore, similar for aliases and other command line tool configs.

So all problems solved?

Not quite.

If I want to add a new tool and it’s not one of the packages available out of the box with nix packages one needs to figure out how to install it.

A macOS specifically annoying part is that while GUI tools can be installed the same way as on arch, e.g. add them to home.packages in home.nix, they’ll not be found by Spotlight. But apparently no matter, nix-darwin may help with that. Something to try once the patience recharged. But you don’t need nix-darwin for user home specific setup and command line tools!