Categories
Node.js Best Practices

Node.js Best Practices — Maintaining Production Code

Spread the love

Node.js is a popular runtime to write apps for. These apps are often production quality apps that are used by many people. To make maintaining them easier, we’ve to set some guidelines for people to follow.

In this article, we’ll look at the best practices for maintaining production code.

Discover Errors and Downtime Using APM Products

Application monitor and performance (APM) products check our codebase and API so that it can go beyond traditional monitoring and measure the overall user experience across services and tiers.

Some of these like Scout can check for repeated and slow database queries for example so that we can look at them and have to fix those issues that are listed.

Otherwise, we may spend lots of time measuring API performance and downtime and we may still miss the slowest code parts in production.

Make Our Code Production-Ready

We should plan for production on day 1 so that we don’t have to worry about issues that only arise after deploying to production.

Measure and Guard Memory Usage

Our apps shouldn’t be using too much memory. Also, we should be aware of memory leaks in our apps. In small apps, we may gauge memory periodically using shell commands, but in medium and large apps we can use more robust monitor tools to watch for memory usage.

An APM tool may help with this as well. For Express apps, we can add the express-status-monitor package to watch the status of our app, including CPU and memory usage, response time, request per second, and more.

We just have to install the package by running:

npm i `express-status-monitor`

Then we can use it by writing the following code:

const express = require('express');
const bodyParser = require('body-parser');

const app = express();
app.use(require('express-status-monitor')());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', (req, res, next) => {
  res.send('hello')
});

app.listen(3000, () => console.log('server started'));

We just put the package straight into our app with app.use(require(‘express-status-monitor’)()); .

Then when we go to the /status page, we’ll see all the performance and health metrics listed.

Get Frontend Assets Out of Node

Frontend assets shouldn’t be in our Node app. Instead, we can host them in their own location with services like S3. This way, our front end code isn’t tightly coupled with our Node app, and it’s also faster since our Node app doesn’t have to hosts these assets in addition to running its own code.

If we move them to another server, then we won’t tie up our Node app by serving hundreds of HTML, CSS, JavaScript, and media files.

Be Stateless and Kill Our Servers Almost Every Day

We should store any kind of data with external data stores. This includes sessions, cookies, cache and uploaded files. We can enforce this by killing and rebuild our servers daily.

The reason we want to do this is to make our app independent of its data. Otherwise, failure in the app’s server will result in downtime instead of just killing the faulty server. Scaling out will also be more challenging because of the reliance on a specific server.

Use Tools that Automatically Detect Vulnerabilities

Tools that detect vulnerabilities helps us check for them without doing any work ourselves. This is good because we don’t want to check all our code and external packages manually since there’s so much code.

These tools warn us if it finds any vulnerabilities so we can fix them soon.

Assign a Transaction ID to Each Log Statement

Assigning transaction IDs to log statements helps us identify each log entry easily as it has a unique ID associated with it. For example, we can log each entry with a unique ID with morgan by using it in conjunction with the uuid module and our own middleware.

For instance, we can write the following:

const express = require('express');
const bodyParser = require('body-parser');
const morgan = require('morgan')
const uuid = require('uuid')
const fs = require('fs');
const path = require('path');

morgan.token('id', (req) => {
  return req.id
})

const app = express();
app.use((req, res, next) => {
  req.id = uuid.v4()
  next()
})
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

const accessLogStream = fs.createWriteStream(path.join(__dirname, 'access.log'), { flags: 'a' })
app.use(morgan(':id :method :url :response-time', { stream: accessLogStream }))

app.get('/', (req, res, next) => {
  res.send('hello');
});

app.listen(3000, () => console.log('server started'));

In the code above, we added our own middleware to set req.id to a UUID. Then we write that to the file along with the response time.

Conclusion

We can add monitoring to our app with APM products. To free our app from unnecessary work, we should move our front end assets out of our Node app. Also, Node apps should be stateless. We can enforce that by killing and rebuilding the server every day. It prevents issues with losing data that we need on a server.

ID should be assigned to log entries so that we can look them up later.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *