Like any kind of apps, JavaScript apps also have to be written well.
Otherwise, we run into all kinds of issues later on.
In this article, we’ll look at some best practices we should follow when writing Node apps.
Use Gzip Compression
We should use gzip compression to compress our assets.
This way, the user needs to download less data to their computers.
We can do that with the compression
middleware with Express:
const compression = require('compression')
const express = require('express')
const app = express()
app.use(compression())
For high traffic sites, we should enable gzip compression in our reverse proxy.
Don’t Use Synchronous Functions
Synchronous functions are definitely a bad idea for many parts of the app.
Any potentially long-running code should be async.
This way, they won’t block the rest of the app from running’
Synchronous calls can add up and slow down our app significantly.
We can use the --trace-sync-io
command-line flag to print warnings and a stack trace whenever synchronous code is run.
This is useful for tracing synchronous code in the development environment.
Do Logging Correctly
To do logging correctly, we should use better loggers than console.log
or console.error
.
Some libraries like Winston can help with logging.
It has much better features than the console
methods.
The debug
module is useful for logging messages for debugging.
We can enable debugging with it with the DEBUG
environment variable.
Handle Exceptions Properly
We can handle exceptions with try-catch or catch
with promises.
Also, we should ensure our app automatically restarts when an unhandled error is encountered.
The best way is to avoid our app crashing.
What we shouldn’t do is to listen to the uncaughtException
event.
It’s emitted when an exception bubbles all the way back to the event loop.
Adding an event listener to the uncaughtException
event will change the default behavior of the process than encounters the exception.
The process will continue to run despite the exception.
Then the state of the process becomes unreliable and unpredictable because of this.
We can use try-catch with synchronous code:
app.get('/search', function(req, res) {
const jsonStr = req.query.params
try {
const jsonObj = JSON.parse(jsonStr)
res.send('Success')
} catch (e) {
res.status(400).send('Invalid JSON')
}
})
We can call catch
with promises:
app.get('/', function (req, res, next) {
queryDb()
.then((data) => {
// ...
return makeCsv(data)
})
.then(function (csv) {
// ...
})
.catch(next)
})
app.use(function (err, req, res, next) {
// handle error
})
We have next
in catch
which is called when an error occurs.
This will call the middleware below it.
Set NODE_ENV to “production”
We should set NODE_ENV
to production
so that Express cache view templates, cache CSS files, and generate less verbose warnings.
These changes will make the app speed up by a factor of 3.
Ensure Our App Automatically Restarts
Our app should automatically restart when an error occurs.
This way, it won’t go offline when it crashes.
We can do this with a process manager.
It would restart the app when the Node app crashes.
The init system provided by our OS may restart the process without a process manager.
Conclusion
We should make some basic changes like gzip compression, ensure automatic restart, and catch errors.