The First 5 Imports
Essential Node/JavaScript Libraries I Can't Live With Out.
When I start a new JavaScript project, it's not uncommon that my first three imports are:
npm install --save lodash async moment
In fact, I find these three libraries so essential, I don't think you'll find a single project I've touched without them. Each library satisfies what I consider a general short coming in the JavaScript core API's. Once you learn to use them, I think you will find them equally essential.
Lodash
Lodash is a general purpose utility library that offers a lot of useful functions working with JavaScript collections, arrays, and object. I highly encourage you study the documentation, because I guarantee you that you will end up writing your own equivalent functions if you don't use the framework.
Lodash is traditionally assigned to a variable named as a single underscore. Lodash is actually a modernization of the popular Underscore library (http://underscorejs.org/).
let _ = require('lodash');
let people = [
{ name: 'Richard', age: 34 },
{ name: 'John', age: 23 },
{ name: 'Jane', age: 65 },
{ name: 'Sophie', age: 42 },
{ name: 'James', age: 18 }
];
// Filtering
let oldPeople = _.filter(people, (person) => person.age > 25);
/*
[
{ name: 'Richard', age: 34 },
{ name: 'Jane', age: 65 },
{ name: 'Sophie', age: 42 }
]
*/
// Finding the first matching object
let john = _.find(people, (person) => person.name === 'John');
/*
{ name: 'John', age: 23 }
*/
// Cloning
let clonedPeople = _.cloneDeep(people);
// Note: there is also a "clone" method that performs a shallow copy
// Merging two objects...I use this a lot with options/configuration
let defaults = { host: '127.0.0.1', port: 3306, database: 'users' },
production = { host: '0.0.0.0' };
// Property precedence is right to left
let config = _.merge(defaults, production);
/*
{ host: '0.0.0.0', port: 3306, database: 'users' }
*/
There is a lot more to Lodash and you should study their docs to get a full idea of what's available. Needless to say, this is probably the most imported dependency I use in JavaScript.
Async
Anybody who has used JavaScript for more than 2 minutes will learn about callback hell. If you are not familiar with the term, it's used to describe the deep nesting of callback functions common when writing asynchronous JavaScript.
The aysnc
framework primarily deals with handling collections and simplifying the control flow of continuations (i.e. callbacks).
For example:
// Realistic Express.js example
module.exports.getArticle =(req, res) => {
Article.find({ _id: req.params.id }, (err, article) => {
if (err) return res.status(500).send({ error: '...'});
if (!article) return res.status(400).send({ error: 'Not found'});
cache.incrementAndGet(req.params.id, (err, views) => {
if (err) return res.status(500).send({ error: '...'});
historyService.trackUserActivity(req.user, req.params.id, (err) => {
if (err) return res.status(500).send({ error: '...'});
return res.status(200).send({ article, views });
});
});
});
};
Lots of deep nesting. What if, however, it was turned into this:
module.exports.getArticle =(req, res) => {
async.parallel({
article: (callback) => {
Article.find({ _id: req.params.id }, callback);
},
views: (callback) => {
cache.incrementAndGet(req.params.id, callback);
},
tracking: (callback) => {
historyService.trackUserActivity(req.user, req.params.id, callback);
}
}, (err, results) => {
if (err) return res.status(500).send({ error: '...'});
if (!results.article) return res.status(404).send({ error: 'Not found' });
res.status(200).send({ article, views });
});
};
While this may not be less code, it's certainly cleaner and has the added benefit of being faster (in the sense that each operation runs in parallel). Also, you'll notice that you get to consolidate the error handling, which can be quite handy.
Please explore this library! There are alternative approaches to control flow (like Promises and Observers), but I find this approach to be the most practical if you are just beginning JavaScript. When you have a grasp on async
, then try bluebird
or q
and RxJS
.
Moment
Working with Dates in JavaScript can be pretty tedious; Moment makes things a lot easier. You can essentially use it as a drop-in replacement for the Date API, but it also contains a lot of really convenient extensions.
let moment = require('moment');
// Create a new moment:
var now = moment();
// Convert the moment into human-relative time.
now.add(2, 'hours').fromNow();
/* "in 2 hours" */
now.subtract(2, 'hours').fromNow();
/* "2 hours ago" */
// Format the moment
now.format("dddd, MMMM Do YYYY, h:mm:ss a");
/* "Friday, February 19th 2016, 1:22:22 am" */
// ISO 8601
now.toISOString();
/* "2016-02-19T09:22:22.614Z" */
// If you do need to convert the moment to a JavaScript Date
// or a UNIX timestamp, it's pretty easy.
now.toDate();
/* "Fri Feb 19 2016 01:22:22 GMT-0800 (PST)" */
now.valueOf();
/* "1455873742614" */
Moment also includes the concept of "durations" which are popular in Date libraries in other languages (like Java's JodaTime). However, I let you learn about it by reading the docs.
Two other incredibly important, but perhaps not always essential libraries I tend to use are Joi and Handlebars.
npm install --save joi handlebars
Joi
Joi is a schema validation framework from the Hapi.js universe providing a simple and pragmatic API.
let BlogPostSchema = Joi.object().keys({
id: Joi.number().integer().min(0),
title: Joi.string().required(),
author: Joi.string().alphanum().min(3).max(20),
content: Joi.string().required(),
posted: Joi.date().iso()
});
let post = {
id: 1,
title: 'The First 5 Imports',
author: 'rclayton',
content: '...',
posted: '2016-02-25T05:39:57.853Z'
};
Joi.validate(post, BlogPostSchema, (err, blogPost) => {
if (err) console.log('Not valid', err);
else console.log(blogPost);
});
Joi's obviously essential if you need to validate input in your application. I highly recommend organizing your inputs into schemas instead of manually validating the input in you application.
Handlebars
My final top 5 import is Handlebars. If you do a lot of JavaScript, you're probably thinking "whaaaaaaat?" For those who are not familiar with Handlebars, it a pretty excellent little templating framework by Yehuda Katz (core Rails contributor and EmberJS author).
When developers think "templating framework", they tend to think of the most popular use case (some kind of web-related MVC). I actually don't use Handlebars for web. For me, I find it most useful when I need to due stuff like email mailers and dynamic configuration:
let emailTemplate = "Hello {{name}},\n\nPlease come back to us!"
+ "\n\nSincerely,\n\nDesparate Salesman",
emailRenderer = Handlebars.compile(emailTemplate);
let body = emailRenderer({ name: 'Richard' });
emailService.sendEmail(to, from, subject, body);
And for configuration, I like to do something like this:
let config = {
host: 'http://localhost:8080',
userCollectionUri = "{{host}}/api/users",
userItemUri = userCollectionUri + "/{{userId}}"
};
// And then I'll inject this into a service.
// Note: I'm going to be lazy and just return the HTTP Response.
class UserService {
constructor(config){
this.host = config.host;
this.userCollectionUriRenderer =
Handlebars.compile(config.userCollectionUri);
this.userItemUriRenderer =
Handlebars.compile(config.userItemUri);
}
getUsers(callback){
let url = this.userCollectionUriRenderer({host: this.host});
request.get(url, callback);
}
getUser(userId, callback){
let url = this.userItemUriRenderer({host: this.host, userId});
request.get(url, callback);
}
}
let userService = new UserService(config);
userService.getUser(1234, (err, response, user) => {
// do something with the user.
});
You may be thinking, "hey, isn't this a huge performance hit because you're using a templating framework for simple string manipulation?" I'm glad you asked! Actually, the Handlebars.compile
method creates a function that essentially does the string manipulation; so don't worry, it's super fast.
Another benefit of using Handlebars is the ability to extend the templating framework by registering helpers, importing other templates, etc.
Summary
Hopefully this was some help for you, especially if you are just getting into Node.js. There's a lot of library options in the ecosystem, and without some guidance, it's bound to trip you up as you start to build applications. Despite the noise, I think you will find that most of these libraries have been consistently popular and widely adopted by developers. If you have more time, I encourage you to browse the "honorable mentions" section below. The libraries listed there are also incredible important and barely missed my top 5.
Honorable mentions
There are a number of other frameworks I use and find essential, but didn't make my top 5 list:
Stumbling my way through the great wastelands of enterprise software development.