Hi all,
the goal of this post on my backend decisions is to help fellow programmers in their decision making or give them some ideas when they want to build something similar. As for me this type of post would have helped me a lot at the beginning.
Backstory: It took me quite a while to decide how I takle the problem to write a new backend. Salvage my old PHP code into a full fledged backend or not? As I wrote everything in pure PHP and used no ORM this was not really fruitful. After a few months of different PHP frameworks jumping I made a full stop and went for a node.js approach. Why node.js? Mainly because I will use javascript on the frontend and it felt for me always more natural. Of course everything will be written in TypeScript, although it still gives me a lot of headache, it does not only prevent me from bugs it is also very helpful in development (at least if you write “good” types).
Goal: node.js backend with lots of business logic already included.
Database: MySQL, the sole and simple reason was that my current app database is running on MySQL. I also have written a few helpful VIEWS which I can reuse quite nicely.
Server: Standard express
configuration with the controllers containerized with awilix
. May drop the containers again, but did not decide yet.
Security:
-
cors
for CORS middleware (for non public API requests) -
helmet
andhpp
to secure http headers -
express-rate-limit
to prevent public API overuses
Authentication: JSON web token strategy (password
). This was the one I could get my head around the “easiest”. The access token is short lived and async frontend calls wait up if the first needs to refresh it. The refresh token is connected with the user-agent to allow multiple tokens for the same user on different devices. I still don’t know how I should handle an expired refresh token, forcefully logout the user or create a password popup.
ORM: knex
was my base and it has a great simple CLI for database migration logic. It also allowed me to write the MySQL VIEWS in raw SQL. As I did not want to use a full grown ORM but little bit of a helper I decided in addition on objection.js
which is the most raw knex
variant I could find. It was a rather simple learning curve and felt correct to use.
Validation: objections.js
allows simple JSON validation, which actually uses ajv-formats
as their base. Nevertheless the first validation (should) already happen at the router with express-validator
(Errors are thrown with boom
). Still undecided if I should use joi
, currently the express-validators seem to be fine for me but joi
would allow better reusability. Did not really start so far with router validation only on the auth routes so this part is still something in development.
Mailer: Standard nodemailer
to handle mailing and mjml
framework for creating the email templates as I can reuse this from my old app.
Internationalisation: After some trial and errors the best way was to simply let the frontend decide which language to use. This means currently the server response contains multiple languages and the frontend displays the correct language based on the user settings. This was a final great idea don’t know where I picked it up.
Datatables.net: I’m a big fan and user of Datatables but will probably drop it because it does not play nicely with vue for my frontend. My current application uses 90% Datatables so this is a big hit for me as I could have salvaged a lot. Now I need to write my own lazy loading tables with export, multi-row edit etc. which probably will cost me some time but at least then all my used libraries are without license fees.
Testing: Nothing so far, I now this is really bad practice but rapid and rough prototyping did not play nicely with a write-test-to-code approach. As most of the stuff I’m coding I do not really understand :).
Small helpful helpers which I believe are always a good choice on bigger projects: dayjs
for date handling, lodash
collection of little helper functions (eg. compare two objects if they are the same), node-schedule
so I don’t need to setup cron-jobs on the server.
What I would do different again? Probably would go currently for Rust
on the backend, simply because I would love to try it and it could come in helpful for future data science projects.
Cheers
Hannes