As software developers, we try to avoid reinventing the wheel. Before we start coding something on our own, we check if it wasn’t done already.
As someone who works with less experienced developers, I see, time and time again, how they struggle to choose the best-suited tool. Because of that, me and my senior colleagues decided to create a list of tools that worked out best for us. That’s how the Node.js tutorial was born.
So, what does Node.js do? First of all, there’s a very big difference between a Node.js server and a PHP server. In PHP, every request is isolated, so errors from one request have no impact on the other ones.
Node.js works differently. If there’s an error in one request, it will have an impact on the whole server.
What’s worse, this impact will probably mean crashing the server and making it unusable.
For that reason, you need a supervisor – a special tool to monitor your process and restart it if necessary. There are multiple such tools available on the market. The most popular ones are forever and supervisor, but in TSH Node.js Team we have only one recommendation: PM2.
PM2 is a Production Runtime and Process Manager for Node.js applications. And, in our opinion, it is exactly what every developer needs.
PM2 has a special, development-only version – PM2-DEV, which makes development easier by restarting your app every time you change something in the code.
PM2 has a built-in load balancer and scaling support (it allows you to run your Node.js app in cluster mode). Plus, it supports deploys (with m2, you can deploy with a simple command). PM2 also has its own docker images in case you want to containerize your app. Finally, it comes with a fully working app dashboard that allows you to monitor the app state (logs, errors, resources usage etc).
We were working with Koa, Loopback, Nest.js, Hapi, Restify, Fastify, Sails.js, Total.js and many, many more, but, in the end, we still decided to go with the good old Express.js.
There are many reasons why we chose Express (for most projects) instead of some other alternative (even rejecting the newer ones).
The first and probably the most important one is the community. Express became so popular that when we talk about Node.js, most people automatically think about Express.
Another advantage is that, unlike Koa, Express is based on the old callback approach. Plus, it isn’t preconfigured for REST APIs – unlike Restify. And, true, Express doesn’t offer such great performance as Fastify does, but the framework makes up for it with a number of extensions and adding new features with ease.
Express is based on middlewares, so if you’re familiar with redux, you’ll feel right at home. Even though, unlike Hapi, the framework doesn’t have a built-in validator, you can use one of many popular middlewares connected with your favourite library (express-validator (ajv), celebrate (Joi)).
The same goes for logging functionality. You can easily connect express to winston, pino or bunyan.
This Node.js tutorial wouldn’t be complete without listing what our usual middleware stack contains:
- winston – for logging purposes
- morgan – for request logging
- cors – cors handling
- helmet – for security purposes
- celebrate – for request validation and error handling
Those are just the most common ones we tend to use.
With another section of Node.js basics covered, let’s talk about testing. We all write tests, or at least I hope so. In order to do it, you need a couple of different tools: something to execute the test (a so-called tests runner), something to assert the results (an assertion library) and, for more advanced cases, something to mock/stub modules (a mocking library).
When I started working with Node.js, I had to install all of these. Back then, I decided to go with a mocha + chai + sinon combo, but I knew there were other possibilities. For example, my frontend colleagues were using Jasmine (as a test runner + assertion library) and Rewire (as a module mocking library).
Then React was released and everything has changed. Facebook gave us a wonderful testing tool that contains everything we need in a single package – Jest. At The Software House, we use Jest for everything – mocking, testing, asserting, on both frontend and backend. And, for now, we see no better alternative available on the market.
Let’s face it. There’s no way to write a bug-free software. It sounds like something you might aspire to, but, in the end, it’s simply impossible. No matter how hard you try to cover as many use cases as possible by testing, you’ll still face the unexpected inside your app.
That’s why every Node.js tutorial should have a debugging chapter. The most important thing is to have the tools that will allow you to find a solution.
Node.js came a long way. It made debugging easy. I remember using console.log to find out what was happening in my code. Then, I switched to node-inspector, which, at some point, was built into the Node core (as –inspect flag). Still, it was nothing compared to the tools I had in PHP.
How many of you had a problem with inspecting the code executed with pm2, ts-node (especially the 5.0+ version)?
A few weeks ago, I found a new library, NDB. This is an advanced debugger from Google – a company behind the V8 engine.
Instead of executing a process with the –inspect flag, you can simply run it with the ndb command. For example, ndb npm start. It will open a completely new instance of chrome with the purpose of debugging only. All core node files are blacklisted by default there, plus you have access to a terminal, REPL and much, much more. It works with standard node, ts-node, pm2 and event pm2 executing typescript. Which means, TSH-approved!
In order to obey all those rules, you should have a tool that will track (and possibly fix) all mistakes.
At TSH, we use a combination of two libraries: eslint or tslint (depending on the language we choose) and Prettier. What’s cool about them is that you can make them work together. Both eslint and tslint are linting tools, meaning they enforce a specific set of rules in your code – for example, disallow var usage.
We tend to connect these with a git hook library (husky + lint staged), so we’re sure that every file is properly formatted and checked before it goes to the repository.
Node.js is still a little behind the current ES version, but, with the latest Node 10, you’re finally able to use native es modules without the need for transpilers. In order to do that, you have to activate an experimental feature and use a special *.mjs extension.
Another thing worth mentioning is static typing, which is getting more and more popular. We’re used to Flow in React components and we’re used to TypeScript in Angular 2+ code, so, naturally, we’re trying to bring it to Node too.
At TSH, we’ve decided to adopt two strategies of project development:
- if possible, we use TypeScript for backend code
Why do we feel the need to have two options? We value flexibility.
Node.js ecosystem is so vast that a young developer might not be able to just dive into it and play with all the features. So, instead of trying out all the available tools, it might be easier to follow the best practices of more experienced developers. At TSH, we value our time, so we choose only the tools that allow us to work efficiently without losing flexibility. I hope that this Node.js tutorial has shed some light on how we do things and helped you get familiar with the technology.