NumTide

DevOps consulting by Developers.

todomvc-nix: One-Stop Solution for Developing Projects Using Nix

Andika Demas Riyandi

2020-12-15

TL;DR; I refreshed the todomvc-nix project, add full-stack Rust, and Haskell frontend. TodoMVC is a project that includes implementations of a simple TODO application, in various languages. The todomvc-nix project takes these examples and shows how to build them using Nix. Both as a developer environment, and build-system. This should give a good starting point for users that want to adopt Nix in their project.

Historical Background

I was introduced to the todomvc-nix repository by zimbatm about 2 years ago, June 26, 2018, to be precise. At that time, I was in the middle of learning Haskell and Nix. As a newcomer, it is hard to find a simple Haskell project example that build using Nix. There are plenty tutorials on using Nix to develop Haskell, though almost all of them are very opinionated to the repository owner and not too beginner-friendly. Hence, as a beginner of both Haskell and Nix, I found it very difficult to read and implement such example to a simple project that I wanted to create.

Because of this complexity and my limited knowledge of Nix, I never managed to implement one Haskell project using it. This is very different with todomvc-nix, because it is very simple and easy to understand. However, todomvc-nix only provides a project example using Haskell as the backend and Javascript as the frontend. There is not any example that show us how to deal with databases. Moreover, CI/CD looks more complicated compared to the development of the CI/CD ecosystem today (e.g. GitHub Actions, Terraform, etc.).

In 2020, I had the idea to upgrade the todomvc-nix in terms of implementation of programming languages, databases, and CI/CD. Since I have been working with zimbatm for several times, especially on open source projects, I proposed this and the refreshment was carried out for approximately 3-4 weeks. Thus, once again, NumTide agreed to let me do the refreshment on a part-time basis (a maximum of 4 hours/day) to refresh the todomvc-nix project.

About todomvc-nix

As previously explained, todomvc-nix is a repository for showing how to manage both the development and production environment of the project made in any programming language using Nix. We use TodoMVC project as an example because it provides various programming languages implementation for a TODO application.

With -nix added behind the todomvc, it means that this project is built specifically for a Nix based project. It should be noted that in this project, the use of Nix is not only for managing programming language tools (compiler, package management, etc.), but also for managing other related devices in a project, such as databases, containers, scripts, web server, and so on.

In the todomvc-nix, I try to cover everything that is related to any real-world project managed by Nix.

Project Updates

Before the update, I noticed that todomvc-nix was good enough for Nix newcomer to learn how to implement Nix in the project. Unfortunately, there are several things that I think need more exploration, such as:

  1. Programming languages used in the old todomvc-nix is very limited (only Haskell and Javascript);
  2. There is no example on database implementations which is very common in an application;
  3. The CI/CD implementation on Nix is confusing;
  4. Lags behind in recent Nix language development (especially the flakes feature)

Accordingly, I am determined to make an update so that every Nix newcomer can easily use Nix, with its up to date feature, to develop their project using their programming language choice.

Implemented full-stack monolith project for the Rust and Haskell programming languages

The first thing that I want to have in todomvc-nix is to show a full-stack project for every existing programming language. Currently, I only familiar with Haskell and Rust, so I try to make an example using these languages. The good news is, it is possible to implement full-stack web application in both Haskell and Rust!

I will not go into more detail on every decision about libraries I used for each Haskell and Rust in this article because it will make the writing very long and tend to be unfocused.

Haskell

Haskell development using Nix can be done in 2 ways, namely: 1) using the default haskellPackages from Nix; or 2) using the haskell.nix developed by HK Input-Output. Another way is to use the built-in nixpkgs provided by libraries that already using Nix (e.g. reflex-dom and miso).

The libraries that I use are not really important or affect the Nix in any ways. I chose these libraries just to challenge myself. For the backend, I use the servant, polysemy, and postgresql-simple. These three libraries are sufficient as the basis for creating TodoMVC application, while the other libraries are very commonly used among haskellers (e.g. text, prelude, etc.).

For the frontend, I use miso, a library that uses ghjcs to compile the Haskell language into Javascript. I don't use reflex (one of the libraries that I am very familiar and happy with) due to several things which will be explained in the future post.

Rust

Rust is the language that I have used the most since NumTide hired me. I love this language because for Haskeller like me, the concepts that Rust presents are very easy to digest and understand.

Rust on the backend uses the sqlx, tide, ructe, and serde libraries. In my opinion, these libraries are very easy to use and understand for beginners.

For the frontend side, I use the wasm library called dominator. Dominator itself is a frontend library that uses Functional Reactive Programming (FRP) concept. I really like FRP for frontend development and always believe that FRP can cure Javascript cumbersomeness. The owner of the library, Pauan, is very active in explaining whenever I ask questions, and doesn't hesitate to provide some solutions when I got stuck.

Implementation of the PostgreSQL database backend

I choose PostgreSQL as a database for todomvc-nix because I was inspired by the postgresql's configuration in the deckdeckgo project that built using Nix. I learned a lot from the repository on how to setup a postgresql development environment using Nix.

For database migration, I use sqitch because I never use the built-in database migration tool from libraries in either Haskell or Rust. In my opinion, sqitch is sufficient for my needs in terms of database migration. Through this database implementation, todomvc-nix can provide an example of using wrapProgram in Nix code.

Using Flake as Nix's newest feature

Flake is a new feature from Nix which aims to improve reproducibility, composability, and usability of a project that uses the Nix ecosystem. Even though it hasn't even shipped to a stable release of Nix yet, I feel it's a good time to refreshed the todomvc-nix project by implementing the flakes feature as it's close to being released in beta. The important part of this refreshment is that a non-flake-nix users don't have to switch to flake immediately and can use the old nix-build and nix-shell commands. Yes, the flake feature needs to be activated first as follows:

Install flake:

$ nix-shell -I nixpkgs=channel:nixos-20.03 --packages nixFlakes

Enable experimental features in file ~/.config/nix/nix.conf::

experimental-features = nix-command flakes

For more information please refer to the README in the todomvc-nix repository.

This upgrade is very important in my opinion because when I started learning Nix, I was very confused about how to use Nix in a simple project. Therefore, as early as possible, when the flakes feature came out, I need to make an example so that Nix beginner or users who don't understand about the flakes feature can understand the feature as soon as possible.

Restructuring the project hierarchy

Another major refresh of the todomvc-nix is about the project structure. I put all of the .nix files into one folder called nix. This is intended to make maintenance and management of project easier to update. In common practice, each project has one default.nix and sometimes shell.nix. So, I make a clear separation in the todomvc-nix folder hierarchy from the start by grouping all of the .nix files into its own nix folder.

My restructuring was actually just separating the smallest part of the todomvc project written in Haskell and Rust into individual folder and having one file default.nix in it. This is intended so that whenever the project need to be updated, user can just look into the nix folder.

For the programming language itself, I separate each language into its respective folder, such as Haskell folder will consists of backend, frontend, and common folder. This the same for Rust and maybe when adding another programming language in the future.

How to Use todomvc-nix

Although the purpose of todomvc-nix is to show how flakes feature works in the Nix project, it still possible to be used by Nix users who don't want to use flakes yet.

Nix users without Flake

For users who haven't activated the Flake feature, they can immediately use nix-build:

$ nix-build -A defaultNix.legacyPackages.x86_64-linux.nix.haskellBackend

and nix-shell:

$ nix-shell

Please refer to the file default.nix for the nix-build command and shell.nix for the nix-shell command.

It is possible to use the nix-build and nix-shell commands on todomvc-nix because we use flake-compat. flake-compat detects flake.nix on a project root directory. The defaultNix attribute on the flake-compat is a starting point derivation and act like common nix-build. For shellNix, the devShell attribute will be used.

Nix users with Flake

Users who have enabled the Flake feature can run the nix build command to run the defaultPackage attribute, nix develop to enter the shell environment in Nix, and to create a specific project, the user can run:

$ nix build .#rust-backend

or

$ nix build .#nix.haskellBackend

For the last command, the . before # means the location of the folder that has flake.nix while nix and haskellBackend are the attribute names that are sourced from the packages attribute in flake.nix

Check it out!!

At the moment, the project only covers Haskell, and Rust as the example languages, and I hope to add more over time (with your help!). Through this post, I invite all Nix users to check the todomvc-nix repo. It will be an honor if you would contribute in developing todomvc-nix through programming languages or technologies that you like.

This todomvc-nix project is not a de facto way for developing applications using Nix, but it should at least provide a sufficient overview for users to start projects using Nix.