eHarmony Engineering logo

Node.js in the Real World

Kaanon MacFarlane

January 21, 2015

At eHarmony we use Node.js® for 100% for our mobile web traffic. We switched to it from a PHP backend that grew stale and unwieldy to develop in. Using JavaScript on both the server- and client-side is an approach with many benefits. The purpose of this post is to share some of the solutions we’ve discovered, so others can make use of what we’ve learned. Kaanon MacFarlane

Infrastructure

Let’s start with our Node.js setup. This is a greatly simplified explanation, but it boils down to a proxy that sends requests to multiple node processes. We use Nginx for the proxy. It looks at each request and decides what to do next. Requests to static assets (like images, css, or client-side JavaScript) are loaded directly from the file system. Requests to pages that haven’t been built in Node are proxied to the original server, such as the PHP cluster that renders our registration pages. Finally, requests that match one of our node.js pages go to the port that the particular process is running on. Each process talks to our cluster of services, which talk to many different data stores (MongoVoldemort, or Oracle).

nodejs infrastructure

Architecture

One of the downsides of our previous application framework was that it became a huge, monolithic creature. We couldn’t change an individual thing, without carefully considering the ramifications in every place. Any manual regressions became a nightmare. It was a bad place to be.

As a result we decided to decouple our features into smaller feature apps. Each section of our site lives in its own repository and runs its own node.js process, listening on an exclusive port.

Now, we can deploy our Match Profile page, with no impact on our Dashboard page and be confident that changes will be confined to one part of the site. We do, however, have some shared bits of code used by multiple features, but that brings us to another wonder of Node.

Package Management

NPM, short for “Node Packaged Modules”, is AWESOME. It acts like ‘yum’ or ‘apt-get’ in that it downloads packages from a central repository, considers each package’s dependencies, and makes the package available for your Node application to use. We include a shared library of common code in each of our feature apps by adding the git repository to our package.json.

Lastly, the one interesting thing about Node that most other web applications don’t have to deal with. As prudent developers, we try to catch expected errors, but sometimes things end up in a state we didn’t expect.  In Node, when there is an uncaught exception, the process exits. We use an npm module called “Forever” to combat this. Forever is installed using “npm -g install forever” (the g is for global). We initiate the Forever script for each of our features, to keep them running. As this is the real world, we need a way to do this without too much manual typing. Our solution is to run Forever as a daemon that can be stopped and started.

Software

With any Node.js application, you can decide exactly how sophisticated you want to make your request processing. Not every use case needs to handle cookies, for instance, so it is added when necessary, instead of making assumptions about what you’ll need.

We decided to use the Express framework, because we find it a useful way to define routes, as well as to handle lots of boilerplate code, like cookie handling or parsing GET and POST parameters. It also doesn’t make assumptions about how a developer might want to render views, so it’s extensible.

To that end, we chose Handlebars as a templating language, mostly because we were already using it on the client-side. We also decided to use Backbone.js for our models. It’s not strictly necessary, but using the same approach for accessing our Models in both the server-side code and the client-side code eliminates the need for developers to mentally switch between contexts as they work on both the server-side and client-side code.