Tag: #actix

Story Stains: Full Stack Enums

Another post about building Story Stains, the previous one can be found at "Story Stains: Frontend".

Enums

Enums are a way of having multiple options programmatically, when a Boolean (true or false) isn't expressive enough. Think traffic lights: Red, Orange, Green, Http Status Codes: Unauthorized, Not Found, OK,..., and Countries: Germany, Poland, Great Britain, England? UK?...

Some things seem obviously able to fit into an Enum data type, until they don't. Emotions are what I was tackling. How many emotions can you feel when you watch a great film? Grief, Fear, Joy, Melancholy. The list may be large but it seems possible to do a suitable search in a dictionary or thesaurus and have a finite list, that's unlikely to change much. So I thought to use an Enum in my backend.

Database storage

How do you store an Enum in a database? Postgres actually supports Enums, but the backend is where the API lives so surely it should be stored there? I decided to use an extra table, I wanted to store a name and a description for each emotion, pretty easy. I'll then have the Enum stored in the backend so I don't need to do database lookups, clever right?

But if the backend is the ultimate source of truth of the enum, how do I keep them in sync? Write a little sync function! So I wrote a little sync function to keep the database in sync with the emotions on start up, easy.

My staging app keeps failing in the sync check...

More Data

Story stains is meant to be somewhat of a mood tracker for the stories you read or watch. What do mood trackers have? Cute little icons to represent the moods. So I need more info in the Enum, using strum I can add many more fields to my Enum, so why not one for an SVG location as well. I could store the whole SVG, but that would only make sense in the database, and that's not the source of truth is it?

Well, when I started adding the emotions to the review API, then I was doing joins on the Emotions table. So for that part of the API the Database table is the source of truth. My sync check seems a little silly now.

More emotions

The first twenty one emotions I decided on were rather extreme: Cruelty, Horror, Betrayal,Disgust,... Reviewing the subtle short story 'I stand here ironing', I'm not sure I felt any of those. I need more emotions, and preferably a quicker way to add them than refactoring an Enum and with a linked migration in the database each time. Time to delete some enums.

Story Stains: The Backend

Another post about building Story Stains, the previous one can be found at "Story Stains: An Idea forms".

Rust

I like to follow trends as much as the next guy, so I'm writing the backend in rust.

There were two main resources that I used:

  • 'Zero To Production In Rust' (zero2prod), a great book about building a backend service from scratch using actix. It's what I based most of my server code on. I enjoyed how each tiny step is broken down and that it's written in a test driven methodology.

  • RealWorld App, a community show case of examples in building a Medium clone. You can see code examples for the backend and frontend based on a single API. In particular a rust version by snamiki1212.

As I wanted to be mobile first, (most of my day to day is only phone after all) I didn't want to build a server side html renderer, as in zero2prod. So I used the API design from the RealWorld App to start.

I also swapped out the session based authentication from the zero2prod in favour of JWT. Likely, it's something I plan to change in the future for an oauth2 implementation. Although any article on Hacker News about authentication has many negative opinions on oauth2 complexity and the ease of badly implementing JWT, so that might change my mind. Generally, I enjoy using my Google account as a login, so I will aim for that in the future.

Mistakes

An aim of Story Stains is to teach myself programming a full stack application with deployment. I expect to make mistakes and learn from them.

One mistake was strictly following the RealWorld App's API design. All of the POST or PUT methods involve sending a wrapper object, i.e.

{
    user: {
        username: 'fred',
        password: 'totallynotarobot'
    }
}

Rather than:

{
    username: 'fred',
    password: 'totallynotarobot'
}

It led me to write, in both the backend and frontend code, a lot of similar into_inner() methods.

Another mistake is using actix or rust in the first place. I chose them because I wanted to learn rust and I enjoyed the zero2prod book, and I have learnt a lot. Often, however, I yearn for the readymade admin interface, pre made queries and ORM from Django. The rust and actix ecosystem doesn't give you nearly as much hand holding and requires you to spend a lot of time fitting parts together.

I found this apparent when adding the emotion feature to the API and found I was writing a lot of repetitive code. I will aim to refactor it out and aim for DRY (don't repeat yourself), but I'm essentially writing part of an ORM. For a one man band or small start-up, that's a lot of work.

Before I felt quite sceptical about no-low-code backends like Firebase and it's competitors, but I can really feel it's appeal. The want to focus on business logic rather than application logic is high.