40 Useful NPM Packages for Node.js Apps in 2021
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.
Table of Contents
Web Frameworks
express
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');
});
app.listen(3000);
fastify
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) => {
reply.type('application/json').code(200);
return { hello: 'world' };
});
fastify.listen(3000, (err, address) => {
if (err) throw err;
fastify.log.info(`App listening on ${address}`);
});
socket.io
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', () => { /* … */ });
});
server.listen(3000);
Utility Functions
async
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
console.log(results);
});
rxjs
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
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
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
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
Validator is a library of string validators and sanitizers.
var validator = require('validator');
validator.isEmail('foo@bar.com'); //=> true
yup
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
schema
.isValid({
name: 'jimmy',
age: 24,
})
.then(valid =>
console.log(valid) // => true
);
// you can try and type cast objects to the defined schema
schema.cast({
name: 'jimmy',
age: '24',
createdOn: '2014-09-23T19:25:25Z',
});
// => { name: 'jimmy', age: 24, createdOn: Date }
day.js
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
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
Jsonwebtoken is a library to sign, verify and decode JSON Web Tokens.
const jwt = require('jsonwebtoken');
const token = jwt.sign({ foo: 'bar' }, 'shhhhh');
bcrypt
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
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
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');
console.log('success!');
} catch (err) {
console.error(err);
}
}
copyFiles();
rimraf
Rimraf provides the equivalent of UNIX rm -rf command for node.
const rimraf = require(“rimraf”);
rimraf('./build', error => {
if (error) console.error(error);
});
mkdirp
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
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
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');
shell.exit(1);
}
// Copy files to release dir
shell.rm('-rf', 'out/Release');
shell.cp('-R', 'stuff/', 'out/Release');
// Replace macros in each .js file
shell.cd('lib');
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);
});
shell.cd('..');
// Run external tool synchronously
if (shell.exec('git commit -am "Auto-commit"').code !== 0) {
shell.echo('Error: Git commit failed');
shell.exit(1);
}
js-yaml
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'));
console.log(doc);
} catch (e) {
console.log(e);
}
Improving Development Process
typescript
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
Jest is complete and ready to set-up JavaScript testing solution
test('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3);
});
winston
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' }),
],
});
logger.log({
level: 'error',
message: 'Hello distributed log files!'
});
logger.info('Hello again distributed logs');
debug
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);
res.end('hello\n');
}).listen(3000, function(){
debug('listening');
});
eslint
ESLint is a tool to find and fix problems in your JavaScript and TypeScript code.
{
"rules": {
"semi": ["error", "always"],
"quotes": ["error", "double"]
}
}
nodemon
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
Dotenv is a zero-dependency module that loads environment variables from a .env
file into process.env
.env
file:
DB_HOST=localhost
DB_USER=root
DB_PASS=s1mpl3
require('dotenv').config();
const db = require('db');
db.connect({
host: process.env.DB_HOST,
username: process.env.DB_USER,
password: process.env.DB_PASS
});
cross-env
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
Commander provides the complete solution for node.js command-line interfaces.
const { program } = require('commander');
program
.option('-d, --debug', 'output extra debugging')
.option('-s, --small', 'small pizza size')
.option('-p, --pizza-type <type>', 'flavour of pizza');
program.parse(process.argv);
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
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
Minimist is a simple library to parse argument options.
const argv = require('minimist')(process.argv.slice(2));
console.log(argv);
chalk
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)
log(chalk.green(
'I am a green line ' +
chalk.blue.underline.bold('with a blue substring') +
' that becomes green again!'
));
// ES2015 template literal
log(`
CPU: ${chalk.red('90%')}
RAM: ${chalk.green('40%')}
DISK: ${chalk.yellow('70%')}
`);
// ES2015 tagged template literal
log(chalk`
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
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
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
@aws-sdk
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);
console.log(results.TableNames.join("\n"));
} catch (err) {
console.error(err);
}
})();
axios
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 } });
console.log(response);
} catch (error) {
console.error(error);
}
}
getUser();
passport
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
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
});
}
main().catch(console.error);
mongoose
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
});
Conclusion
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.