Lesson 27-Node Performance Optimization and Application Security

Error Handling

Handling Uncaught Exceptions

  • Unless developers explicitly add .catch statements, errors thrown in these areas will not be handled by the uncaughtException event handler and will silently disappear.
  • While Node applications won’t crash, this can lead to memory leaks.
process.on('uncaughtException', (error) => {
  // I just received an unhandled error
  // Now handle it and decide whether to restart the application
  errorManagement.handler.handleError(error);
  if (!errorManagement.handler.isTrustedError(error)) {
    process.exit(1);
  }
});

process.on('unhandledRejection', (reason, p) => {
  // I just caught an unhandled promise rejection
  // Since we already have a fallback mechanism for unhandled errors (see below)
  // Throw it to let the fallback handle it
  throw reason;
});

Managing Exceptions with Domain

  • Create an instance using the create method of the domain module.
  • Any error, along with other errors, will be handled by the same error-handling method.
  • Any code that causes errors within this callback will be covered by the domain.
  • Allows code to run in a sandbox and provides feedback to users using the res object.
const domain = require('domain');
const audioDomain = domain.create();

audioDomain.on('error', function (err) {
  console.log('audioDomain error:', err);
});

audioDomain.run(function () {
  const musicPlayer = new MusicPlayer();
  musicPlayer.play();
});

Joi Parameter Validation

const memberSchema = Joi.object().keys({
  password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/),
  birthyear: Joi.number().integer().min(1900).max(2013),
  email: Joi.string().email(),
});

function addNewMember(newMember) {
  // Assertions come first
  Joi.assert(newMember, memberSchema); // Throws if validation fails

  // Other logic here
}

Kibana System Monitoring

See: Smart Logging Best Practices

Deployment Practices

Logging with Winston

const winston = require('winston');
const moment = require('moment');

const logger = new (winston.Logger)({
  transports: [
    new (winston.transports.Console)({
      timestamp: function () {
        return moment().format('YYYY-MM-DD HH:mm:ss');
      },
      formatter: function (params) {
        let time = params.timestamp(); // Time
        let message = params.message; // Manual message
        let meta = params.meta && Object.keys(params.meta).length ? '\n\t' + JSON.stringify(params.meta) : '';
        return `${time} ${message}`;
      },
    }),
    new (winston.transports.File)({
      filename: `${__dirname}/../winston/winston.log`,
      json: false,
      timestamp: function () {
        return moment().format('YYYY-MM-DD HH:mm:ss');
      },
      formatter: function (params) {
        let time = params.timestamp(); // Time
        let message = params.message; // Manual message
        let meta = params.meta && Object.keys(params.meta).length ? '\n\t' + JSON.stringify(params.meta) : '';
        return `${time} ${message}`;
      },
    }),
  ],
});

module.exports = logger;

// logger.error('error')
// logger.warn('warn')
// logger.info('info')

Delegating to a Reverse Proxy

Node performs poorly with CPU-intensive tasks like gzipping and SSL termination. Instead, use a dedicated middleware service like Nginx, which handles these tasks more efficiently. Otherwise, Node’s single-threaded nature will be bogged down by network tasks, reducing performance and impacting the application core.

While express.js can serve static files via Connect middleware, this is not recommended. Nginx handles static files more effectively and prevents requests for dynamic content from blocking the Node process.

# Configure gzip compression
gzip on;
gzip_comp_level 6;
gzip_vary on;

# Configure upstream
upstream myApplication {
  server 127.0.0.1:3000;
  server 127.0.0.1:3001;
  keepalive 64;
}

# Define web server
server {
  # Configure server with SSL and error pages
  listen 80;
  listen 443 ssl;
  ssl_certificate /some/location/sillyfacesociety.com.bundle.crt;
  error_page 502 /errors/502.html;

  # Handling static content
  location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) {
    root /usr/local/silly_face_society/node/public;
    access_log off;
    expires max;
  }
}

Detecting Vulnerable Dependencies

See: npm audit documentation

PM2 HTTP Cluster Configuration

Worker Thread Configuration

  • pm2 start app.js -i 4: The -i 4 flag runs the app in cluster mode with 4 worker threads. If set to 0, PM2 will spawn threads based on the CPU core count.
  • PM2 automatically restarts worker threads if they crash.
  • pm2 scale <app name> <n>: Scales the cluster.

PM2 Auto-Start

  • pm2 save: Saves the current running applications.
  • pm2 startup: Configures auto-start.

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here

Share your love