12 min read

40 Useful NPM Packages for Node.js Apps in 2021

Andriy Obrizan

Long ago, software engineers realized they could significantly speed up the development process by eliminating the need to write repetitive code in every application over and over again.

Node.js applications benefit from more than a million open-source packages available in the NPM package registry. Most popular packages get well over 10 million weekly downloads and are at the foundation of many applications, from small pet projects to well-known tech startups. Today, 97 percent of code in modern web applications comes from npm modules.

We’ll briefly cover the popular ones that will save you from reinventing the wheel.

Web Frameworks


Express is the most popular, fast, and minimalist web framework for node.js backends.

const express = require('express');

const app = express();

app.get('/', function (req, res) {
  res.send('Hello World');



Fastify is one of the fastest extendible web frameworks focused on providing the best developer experience with the least overhead.

const fastify = require('fastify')({
  logger: true

fastify.get('/', async (request, reply) => {
  return { hello: 'world' };

fastify.listen(3000, (err, address) => {
  if (err) throw err;

  fastify.log.info(`App listening on ${address}`);


Socket.IO enabled real-time bidirectional event-based communication using long-polling or WebSockets with disconnection detection and auto-reconnection support.

const server = require('http').createServer();
const io = require('socket.io')(server);

io.on('connection', client => {
  client.on('event', data => { /* … */ });
  client.on('disconnect', () => { /* … */ });


Utility Functions


Async is a module with powerful utility functions for working with asynchronous JavaScript.

const async = require("async");

async.mapLimit(urls, 5, async function(url) {
    const response = await fetch(url);
    return response.body;
}, (err, results) => {
    if (err) throw err;

    // results is now an array of the response bodies


RxJS is a modular set of libraries to compose asynchronous and event-based programs using observable collections and compositions in JavaScript.

const { range } = require('rxjs');
const { map, filter } = require('rxjs/operators');

range(1, 200).pipe(
  filter(x => x % 2 === 1),
  map(x => x + x)
).subscribe(x => console.log(x));


Lodash is a utility library that makes JavaScript easier by taking the hassle out of working with arrays, numbers, objects, strings, etc.

const _ = require("lodash");

const nums = _.range(1, 9);
// => [1, 2, 3, 4, 5, 6, 7, 8, 9]

const chunks = _.chunk(nums, 3);
// => [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

const right = _.takeRight(nums, 2);
// => [7, 8, 9]


Underscore.js is a utility-belt library for JavaScript that provides support for the usual functional suspects (each, map, reduce, filter, etc.) without extending any core JavaScript objects.

const _ = require(“underscore);

const list = [[5, 1, 7], [3, 2, 1]];

_.invoke(list, 'sort');
// => [[1, 5, 7], [1, 2, 3]]


Ramda is a practical, functional library with side-effect free functions composable with currying.

import * as R from 'ramda';

const double = x => x * 2;

R.map(double, [1, 2, 3]); 
// => [2, 4, 6]

R.map(double, {x: 1, y: 2, z: 3}); 
// => {x: 2, y: 4, z: 6}


Validator is a library of string validators and sanitizers.

var validator = require('validator');

validator.isEmail('foo@bar.com'); //=> true


Yup is a schema builder for complex, interdependent validations and transformations.

import * as yup from 'yup';

let schema = yup.object().shape({
  name: yup.string().required(),
  age: yup.number().required().positive().integer(),
  email: yup.string().email(),
  website: yup.string().url(),
  createdOn: yup.date().default(function () {
    return new Date();

// check validity
    name: 'jimmy',
    age: 24,
  .then(valid => 
    console.log(valid) // => true

// you can try and type cast objects to the defined schema
  name: 'jimmy',
  age: '24',
  createdOn: '2014-09-23T19:25:25Z',
// => { name: 'jimmy', age: 24, createdOn: Date }


Day.js is a minimalist JavaScript library that parses, validates, manipulates, and displays dates and times for modern browsers with a largely Moment-compatible API.

const dayjs = require(“dayjs”);

dayjs().startOf('month').add(1, 'day').set('year', 2018).format('YYYY-MM-DD HH:mm:ss');


Date-fns provides the most comprehensive, yet simple and consistent toolset for manipulating JavaScript dates in a browser & Node.js.

import { format, formatDistance, formatRelative, subDays } from 'date-fns'

format(new Date(), '[Today is a] dddd')
//=> "Today is a Wednesday"

formatDistance(subDays(new Date(), 3), new Date())
//=> "3 days ago"

formatRelative(subDays(new Date(), 3), new Date())
//=> "last Friday at 7:26 p.m."


Jsonwebtoken is a library to sign, verify and decode JSON Web Tokens.

const jwt = require('jsonwebtoken');

const token = jwt.sign({ foo: 'bar' }, 'shhhhh');


Bcrypt is a library to hash and verify passwords with sync, callbacks, and promise interface.

const bcrypt = require('bcrypt');
const saltRounds = 10;
const myPlaintextPassword = 's0/\/\P4$$w0rD';

bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
    // Store hash in your password DB.


UUID is a library to create RFC4122 universally unique identifiers.

const { v4: uuidv4 } = require('uuid');

uuidv4(); // => '1a68a438-b077-468b-b1e8-dcdd976a0f5b'

Working With File System


FS-extra adds file system methods that aren’t included in the native fs module and adds promise support to the fs methods.

const fs = require(‘fs-extra’);

async function copyFiles () {
  try {
    await fs.copy('/tmp/myfile', '/tmp/mynewfile');
  } catch (err) {



Rimraf provides the equivalent of UNIX rm -rf command for node.

const rimraf = require(“rimraf”);

rimraf('./build', error => {
  if (error) console.error(error);


Just like mkdir -p, mkdirp recursively creates the directory and all necessary subdirectories.

const mkdirp = require('mkdirp')

// return value is a Promise resolving to the first directory created
mkdirp('/tmp/foo/bar/baz').then(made =>
  console.log(`made directories, starting with ${made}`));


Glob is a library to match files using multiple patterns.

const glob = require("glob");

// options is optional
glob("**/*.js", options, function (er, files) {
  // files is an array of filenames.
  // If the `nonull` option is set, and nothing
  // was found, then files is ["**/*.js"]
  // er is an error object or null.


ShellJS is a portable (Windows/Linux/OS X) implementation of Unix shell commands on top of the Node.js API.

const shell = require('shelljs');

if (!shell.which('git')) {
  shell.echo('Sorry, this script requires git');

// Copy files to release dir
shell.rm('-rf', 'out/Release');
shell.cp('-R', 'stuff/', 'out/Release');

// Replace macros in each .js file
shell.ls('*.js').forEach(function (file) {
  shell.sed('-i', 'BUILD_VERSION', 'v0.1.2', file);
  shell.sed('-i', /^.*REMOVE_THIS_LINE.*$/, '', file);
  shell.sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, shell.cat('macro.js'), file);

// Run external tool synchronously
if (shell.exec('git commit -am "Auto-commit"').code !== 0) {
  shell.echo('Error: Git commit failed');


Js-yaml is an implementation of YAML, a popular human-friendly data serialization language.

const yaml = require('js-yaml');
const fs   = require('fs');

// Get document, or throw exception on error
try {
  const doc = yaml.load(fs.readFileSync('/home/ixti/example.yml', 'utf8'));
} catch (e) {

Improving Development Process


TypeScript is JavaScript that scales. It’s a language that adds optional types and compiles to plain readable JavaScript.

interface User {
  name: string;
  id: number;

const user: User = {
  name: "Hayes",
  id: 0,


Jest is complete and ready to set-up JavaScript testing solution

test('adds 1 + 2 to equal 3', () => {
  expect(1 + 2).toBe(3);


Winston is a simple and universal logging library with support for multiple transports.

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  defaultMeta: { service: 'user-service' },
  transports: [
    // Write all logs with level `error` and below to `error.log`
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    // Write all logs with level `info` and below to `combined.log`
    new winston.transports.File({ filename: 'combined.log' }),

  level: 'error',
  message: 'Hello distributed log files!'

logger.info('Hello again distributed logs');


Debug is a tiny JavaScript debugging utility modeled after Node.js core’s debugging technique.

const debug = require('debug')('http')
  , http = require('http')
  , name = 'My App';

debug('booting %o', name);

http.createServer(function(req, res){
  debug(req.method + ' ' + req.url);
}).listen(3000, function(){


ESLint is a tool to find and fix problems in your JavaScript and TypeScript code.

    "rules": {
        "semi": ["error", "always"],
        "quotes": ["error", "double"]


Nodemon is a tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected.

nodemon ./server.js 


Dotenv is a zero-dependency module that loads environment variables from a .env file into process.env

.env file:


const db = require('db');

  host: process.env.DB_HOST,
  username: process.env.DB_USER,
  password: process.env.DB_PASS


Cross-env enables scripts to set and use environment variables across platforms.

  "scripts": {
    "start-prod": "cross-env NODE_ENV=production node ./app.js"

Better Command Line Interface


Commander provides the complete solution for node.js command-line interfaces.

const { program } = require('commander');

  .option('-d, --debug', 'output extra debugging')
  .option('-s, --small', 'small pizza size')
  .option('-p, --pizza-type <type>', 'flavour of pizza');

const options = program.opts();
if (options.debug) console.log(options);
console.log('pizza details:');
if (options.small) console.log('- small pizza size');
if (options.pizzaType) console.log(`- ${options.pizzaType}`);


Yargs helps you build interactive command-line tools by parsing arguments and generating an elegant user interface.

const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const argv = yargs(hideBin(process.argv)).argv;

if (argv.ships > 3 && argv.distance < 53.5) {
  console.log('Plunder more riffiwobbles!');
} else {
  console.log('Retreat from the xupptumblers!');


Minimist is a simple library to parse argument options.

const argv = require('minimist')(process.argv.slice(2));



Chalk brings styling to the terminal.

const chalk = require('chalk');
const log = console.log;

// Combine styled and normal strings
log(chalk.blue('Hello') + ' World' + chalk.red('!'));
// Compose multiple styles using the chainable API
log(chalk.blue.bgRed.bold('Hello world!'));
// Pass in multiple arguments
log(chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz'));
// Nest styles
log(chalk.red('Hello', chalk.underline.bgBlue('world') + '!'));
// Nest styles of the same type even (color, underline, background)
    'I am a green line ' +
    chalk.blue.underline.bold('with a blue substring') +
    ' that becomes green again!'

// ES2015 template literal
CPU: ${chalk.red('90%')}
RAM: ${chalk.green('40%')}
DISK: ${chalk.yellow('70%')}

// ES2015 tagged template literal
CPU: {red ${cpu.totalPercent}%}
RAM: {green ${ram.used / ram.total * 100}%}
DISK: {rgb(255,131,0) ${disk.used / disk.total * 100}%}

// Use RGB colors in terminal emulators that support it.
log(chalk.keyword('orange')('Yay for orange colored text!'));
log(chalk.rgb(123, 45, 67).underline('Underlined reddish color'));
log(chalk.hex('#DEADED').bold('Bold gray!'));


Colors is a library to use colors and styles in the node.js console.

const colors = require('colors');

console.log('hello'.green); // outputs green text
console.log('i like cake and pies'.underline.red) // outputs red underlined text
console.log('inverse the color'.inverse); // inverses the color
console.log('OMG Rainbows!'.rainbow); // rainbow
console.log('Run the trap'.trap); // Drops the bass


Ora is an elegant terminal spinner.

const ora = require('ora');

const spinner = ora('Loading unicorns').start();

setTimeout(() => {
	spinner.color = 'yellow';
	spinner.text = 'Loading rainbows';
}, 1000);

Other Specialized Packages


Amazon Web Serviced SDK for JavaScript and TypeScript. V3 has a modular architecture with a separate package for each service.

const { DynamoDBClient, ListTablesCommand } = require("@aws-sdk/client-dynamodb");

(async () => {
  const client = new DynamoDBClient({ region: "us-west-2" });
  const command = new ListTablesCommand({});
  try {
    const results = await client.send(command);
  } catch (err) {


Axios is a Promise based HTTP client for the browser and Node.js.

const axios = require('axios');

async function getUser() {
  try {
    const response = await axios.get('/user', { params: { ID: 12345 } });
  } catch (error) {



Passport is Express-compatible authentication middleware for Node.js with 480+ strategies.

passport.use(new LocalStrategy(
  function(username, password, done) {
    User.findOne({ username: username }, function (err, user) {
      if (err) { return done(err); }
      if (!user) { return done(null, false); }
      if (!user.verifyPassword(password)) { return done(null, false); }
      return done(null, user);


Nodemailer is a module for Node.js applications to allow easy as cake email sending.

const nodemailer = require("nodemailer");

async function main() {
  const testAccount = await nodemailer.createTestAccount();

  const transporter = nodemailer.createTransport({
    host: "smtp.ethereal.email",
    port: 587,
    secure: false, // true for 465, false for other ports
    auth: {
      user: testAccount.user, // generated ethereal user
      pass: testAccount.pass, // generated ethereal password

  await transporter.sendMail({
    from: '"Fred Foo 👻" <foo@example.com>', // sender address
    to: "bar@example.com, baz@example.com", // list of receivers
    subject: "Hello ✔", // Subject line
    text: "Hello world?", // plain text body
    html: "<b>Hello world?</b>", // html body



Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment.

const mongoose = require(“mongoose”);
const Schema = mongoose.Schema;
const ObjectId = Schema.ObjectId;

const BlogPost = new Schema({
  author: ObjectId,
  title: String,
  body: String,
  date: Date


Every good Node.js developer should be familiar with the most popular NPM packages.

Before writing some piece of code, try to think if you’re solving a business problem or your specific case is that unique. With the amount of open-source code in the registry, there’s a high chance that someone already created a package for that. Searching for it is always worth the effort.

People push thousands of packages to NPM every day, and not all of them are robust. You have to be careful with your application’s dependencies. Packages with a proven track record, high weekly downloads, recent commit history, unit tests, GitHub stars, and several contributors are usually a safe bet.

With third-party code, however, you can’t be 100% sure all the time.