Quick and simple dot-voting with Dot dot vote

I recently launched Dot dot vote, a simple web application for running anonymous dot-voting polls. Dot-voting is a quick and simple method for prioritizing a long list of options. I find them to be quite useful in when planning software development projects. Every team I have ever worked with has had far too many potential projects than they have people or time, dot voting can help customers and stakeholders weigh in on which of the projects are most valuable to them. Dot dot vote makes it trivial to create short-lived polls which don’t require any user registrations, logins, or overhead.

Dot dot vote

The desire for the application came out of some of my own roadmap planning exercises for Scribd. Unfortunately I didn’t finish the application in time and had to hack together something unpleasant with Google forms. Next time around, the tool will be ready!

Give it a try and let me know what you think!

Technologies

Dot dot vote is free and open source software, and is quite simple. The backend is built with Rust using the minimalistic Tide web application framework and sqlx for database (PostgreSQL) access. The “frontend” is plain old HTML and some basic JavaScript being rendered via handlebars-rust. The application is currently deployed on Digital Ocean’s new “App Platform”, which I won’t discuss in this blog post except to say that it is interesting in both good and bad ways.

“this might be the most complete/functioning oss tide app in existence”

– Co-maintainer of Tide

This isn’t my first Rust application which has reached “production” status, hotdog deserves that honor. It is however the first non-trivial web application I have built from top to bottom in years. Much of the time spent on implementation was just getting familiar with the tools. With that experience under my belt, I actually built another (non-public) web application of similar scope in about a day of frenzied coding; Rust is web-ready as far as I am concerned!

There are a couple tips I would like to share for anybody wishing to explore using these tools for their own projects.

cargo sqlx prepare

I made heavy use of the query! and query_as! macros in sqlx, which both do compile-time SQL query syntax validation against your live database schema. It’s incredibly useful, but it has the downside of requiring a live database connection for in CI and other builds.

The experimental sqlx-cli has an integration with cargo that allows you to generate a sqlx-data.json file which the macros can use instead of a live connection.

Despite being experimental and arguably a bit bleeding edge, I had no issues with using the latest master branch from sqlx.

handlebars-rust template reload in dev

Dot dot vote uses the register_templates call in handlebars-rust to load the templates in the views/ directory. For obvious performance reasons this reads everything once, compiles, and stores the templates in memory. In development this is not very useful, since you have to restart the web server any time you make a minor view change.

The approach that I took was to provide a render() function on the AppState struct which would abstract Handlebars template rendering for the routes. Then, in order to allow for “reloading” of templates in development/debug builds, I used the following snippet:

    pub async fn register_templates(&self) -> Result<(), handlebars::TemplateFileError> {
        let mut hb = self.hb.write().await;
        hb.clear_templates();
        hb.register_templates_directory(".hbs", "views")
    }

    pub async fn render(
        &self,
        name: &str,
        data: &serde_json::Value,
    ) -> Result<tide::Body, tide::Error> {

        // Reload all the templates in debug builds on each render call
        #[cfg(debug_assertions)]
        {
            self.register_templates().await;
        }

        // etc
    }

Overall the project was fun exploration into the web world of Rust. I would strongly recommend the Tide/sqlx/handlebars-rust stack for building small and fast API and web services. The stripped release binary is ~4MB and only takes up 30MB at runtime in production. The most basic tier in Digital Ocean is way over-provisioned!

With this little side project “complete”, it’s back to other fun Rust hacking!