Introduction to Node.js

Introduction

If you are familiar with web development, you know that mixing development tasks between server side (PHP, Python, etc) and client side (mostly Javascript) can be a little difficult sometimes. We face challenges regarding programming languages (for example, PHP is very different from Javascript) and we also need to create a way for both sides of our application to communicate.

Most web applications run using the classic LAMP approach where server side is running Apache and using PHP as the scripting language. Today we are going to discover a new type of server that is being used by a lot of big companies: Node.js.

Node.js is a server-side platform built on Google Chrome's JavaScript Engine (V8 Engine) which lets us create our server using JavaScript. One of the reasons Node.js is popular is because it enables our web applications to use the same language for both server and client sides. Let’s see some of the main differences between Apache and Node.js.

Node.JS And Apache

One of the main differences between Node.js and Apache is that Apache is thread and process based. This means that Apache handles every request on its own process or thread (depending on the configuration), so if the request is waiting for some resource (a database query, some I/O operation like opening a file, etc) the whole thread will be blocked. But only the thread which performs the request is blocked so this will not block our entire web application.

On the other hand, Node.js runs in a single thread process, but can handle multiple requests because they can be asynchronous (so it can generally handle more requests per second than Apache). But, what happens if some error occurs? Well, if we do not handle it correctly (for example, a database query fails because our database server is down at the moment) the whole Node.js server will collapse, and any global data such as session user information is going to be lost. Don’t worry though, because Node.js has mechanisms which allow us to create fallbacks when something fails, or even restart a new Node.js server when an error occurs.

The Node.js asynchronous behavior must be kept in mind always, and we have to develop our code to work with it. So in terms of computer resources, if we have an intensive task doing I/O operations (for example, we need to dump a lot of data into a file), this will increase the CPU usage of our server (remember that Node.js is single thread); a good practice might be to move this task to a worker so our main Node.js server will not hang out and we can keep receiving requests without problems.

INSTALLING NODE.JS

To install Node.js, we can download their code and compile it ourselves or install it directly from a repository. We are going to use the second option in this tutorial, but feel free to download it and compile it by yourself if you feel confident.

Node.js installation is quite similar to MongoDB installation: we add a new repository and execute the apt command to install the package.

Node.js is friendlier because they provide us with a script which will look for the best repository for our system. Using the following command will automatically read this script and execute it:


$ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -

NOTE: if you are curious, you can visit the url and take a look at the script. You might learn from it.

As we add the repository, we just install it using apt as usual:


$ sudo apt-get install -y nodejs

Node.js has no dependencies, but some npm packages will probably throw some errors when compiling. To fix this, we need to install the build-essentials package as well:


$ sudo apt-get install build-essential
First steps

Let’s write our first Node server. Open your favorite editor, copy the following code and save it as server.js:


const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World\n');
});

server.listen(port, hostname, () => {
console.log('Server running at http://${hostname}:${port}/');
});


Now we need to run our server. To do this, go to a terminal window, change to the folder where you saved the previous file (using the cd command) and type:


$ node server.js

You will see a message that your server is running. If you go to http://localhost:3000 in your browser, a web page will appear with the “Hello world” message.

Explanation

We created a very tiny Node.js server that prints a message when a user visits the url. To do this, we include a Node package called “http” that allows us to create a server and manage its configuration:


const http = require(‘http’);

In Node.js we import libraries or other files using the “require” statement. Node.js has a lot of packages included by default, so you can link them for your own purposes. You can also use code from other people and include it in the same way (we will see how to do this using npm later on).

After that, we defined some constants such as our server IP and port. This is always a good practice, and you can also define a JSON file with configurations for different environments, and load it conditionally.

Now we are going to create our server. In this case the server will route our users to a single call which will print a message. Advanced Node.js servers create one function per each HTTP operation (POST, GET, UPDATE and DELETE), and it is recommended that servers handle operations in this way (in further guides we will see how to create advanced servers)

Every handler operation function in Node.js has at least two parameters: the request and the response. The request is used to get information about additional parameters passed by the client to our server. For example, if someone visits this url:

http://www.ourserver.com/clients?id=q1w2e3r4

The URL is giving us a parameter in the URL itself (in this case, a client identifier). Node.js allows us to get these parameters to do some logic (for example, query our database based on an ID).

We can also send the results of our server logic (or send a “response”) using the response parameter.

In this example we need to create a webpage with our desired message, so we set an HTTP valid status code, the type of the message and the message itself. As you can see, this is a rudimentary way to create a web page for our users. A better way is to use Node to send some status code as JSON, and let our client side handle the website generation (for example, using Angular).

Using packages: npm

When we need some third-party code, we have to download the code, create the files and copy them into our project folder. In Node.js this task becomes super easy with npm.

npm is a package manager for JavaScript commonly used with Node.js. With a few commands, we can install and update libraries from other people, which will enhance our project in different ways. Some examples of npm packages are:

  • s3: Amazon S3 high level client for handling S3 uploads (retries, secure requests, progress report, etc).
  • sharp: high performance image processing to manipulate images (resize, crop, extract regions, etc).
  • async: powerful library with functions to allow us asynchronous operations (multiple functions in parallel, mapping, execute a function until something occurs, create queues, etc).
  • mongoose: schema validation, high level functions for saving and much more for MongoDB.

To accomplish this, npm creates a file inside our project’s root folder called package.json. This file contains JSON with all the package dependencies that our project needs. A package.json example might look like this:


{
"name": "project",
"description": "My first project with Node.js",
"version": "0.0.1",
"author": "John Doe <john@doe.net>",
"dependencies": {
"express": "^4.2.0",
"lodash": "^2.4.1",
"moment": "^2.10.3",
"mongoose": "3.8.23",
"passport": "^0.2.0",
"s3": "4.4.0",
}
}

As you can see, package versions can have different characters at the beginning. This represents the way that npm will keep our packages updated. You can read more about this feature in npm documentation page.

One assumption that we may have is that all of these packages are maintained and developed by the npm team, and this is not true. Npm doesn’t provide support for any of the packages that they host because they didn’t develop them in the first place. In addition, you can see npm package code in the package’s Github repository. You can also contribute by sending pull requests or identifying issues if you find any.

How to use npm

Let’s suppose that we are developing a project using Node.js, and we need to support file uploads to Amazon S3. We found the s3 package, and we want to use it so let’s install it:


$ sudo npm install s3 --save

In this case, the s3 package is exactly what we want, so we add the --save option. With this option, npm is going to add it to our packages.json file as a dependency. This option is not a must at all; you can install a package without this option, do some test and if it fits your purposes, run the same command with --save option to add it to the packages.json file.

If we have a fresh copy of a Node.js project with a packages.json file, we can install every dependency using:


$ sudo npm install

This will parse the packages.json file, and install the packages that we don't have. If we need to uninstall a package, we just do this:


$ sudo npm uninstall s3

In the same way, if we pass the parameter --save the package will be uninstalled and removed from our packages.json. This is very useful if we added it before, and we don’t want it anymore on our project.

But what happens if we want to do a clean install of all packages, but we already have them? If you take a look at your project’s folder, you will see a folder called node_modules. That folder is where all npm packages are, so if we need to do a fresh install, we can remove that folder and install all over again:


$ sudo rm -rf node_modules
$ sudo npm install


The implications of using npm is that we don’t need to have all of these npm packages with the project code itself. So if we are using some control version system like git, we can ignore the node_modules folder by editing the .gitignore file and pushing our code to the repo. If someone clones the repository, just doing sudo npm install directly on the project’s root folder will install all of the dependencies.

npm is a very powerful tool with a lot of different parameters and use cases. You can read further information about npm on their documentation page.

Setting server configuration

When we are working with web deployments, one common practice is to create different environments with different configurations; so if we need to switch and test things we don’t need to modify almost anything in our project.

One of the very first things that you should do is to identify your project parameters. The most common are:

  • Server IP and port.
  • Database IP and port.
  • Database credentials.
  • API keys for different third party services, such as AWS, Facebook, Google, etc.

But how can we load different configuration depending on which machine (development, production) our project is running on?

To do this, we can define a JSON file with our environments and their parameters. For example, if we have two environments (development and production), we create a file called config.json on our project root folder with this information:


{
"development": {
"web_server": {
"ip": "192.168.1.34",
"port": 3000
},
"db_server": {
"ip": "192.168.1.34",
"port": 47017
},
"s3": {
"access_key_id": "Q1W2E3R4T5Y6U7I8P9",
"secret_key_id": "Q1w2E3r4T5+y6U7i8O9p0",
"bucket": "project-dev",
"domain": "https://static-dev.project.com/"
}
},
"production": {
"web_server": {
"ip": "88.14.139.46",
"port": 4000
},
"db_server": {
"ip": "88.14.139.46",
"port": 68012
},
"s3": {
"access_key_id": "Q1W2E3R4T5Y6U7I8P9",
"secret_key_id": "Q1w2E3r4T5+y6U7i8O9p0",
"bucket": "project-master",
"domain": "https://static.project.com/"
}
},
}

We set two different environments with different configuration parameters. Now we need to load them conditionally. We are going to modify our server.js file and add the following at the beginning:


var env = require(__dirname + '/config.json');
var config;
if (process.env.NODE_ENV === undefined)
config = env.development;
else
config = env[process.env.NODE_ENV];


With this piece of code, we are loading our configuration file (that we called config.json) conditionally. After this, all our parameters will be in the config variable; for example, if we need to access our server port, we can do config.port.

We are using a Node.js variable called NODE_ENV to do this conditional load process. Every Node.js server has an object called process with contains different information for the server. One of them is NODE_ENV which we can use to set the environment when we start the server.

So if we initialize our server like this:


$ NODE_ENV=development node server.js

Our Node.js server will load into the variable config the configuration parameters that we defined before for development environment. In the same way we can change that NODE_ENV value for production to load the production parameters.

This gives us a lot of freedom when we need to test things in different environments. If something needs to be changed anytime (for example, our database server port) we don’t need to look for their uses: we just change the specific environment variable in our configuration file.

Testing our server

There are many different ways to test our server functionality. If we are developing a website, we can visit the site that we want and check if our server is working as we expected.

But maybe not all of our server logic will be formatted as a web page because we are returning values from the database. For these cases we can use different tools such as Postman or curl which will make calls and return the data in a more readable way.

Postman is a tool that can be used as a Chrome application that let us make calls with parameters to web servers. You can specify methods (POST, GET, etc), parameters and much more. You can also save those queries for later uses or export them.

On the other hand, a more known tool that comes already installed in most of Unix systems is curl. Using your terminal, you can make requests using different methods and even authentication. This is very useful when you can only make requests using local interfaces, and you don’t have graphical interface (for example, if you connect to your AWS EC2 instance with Node.js installed, and server calls are restricted to localhost only).

Conclusion

  • We have learned what Node.js is, and the main differences with Apache.
  • We have learned how to install Node.js.
  • We have created our first demo server.
  • We have learned more about npm and how to install packages.
  • We have known two tools (Postman and curl) which will help us on our tests.

What’s next

This was a beginner introduction to Node.js. We created a very simple server, but most production servers won’t be that easy. The Node.js world is really big, and we can find a lot of packages to handle our calls, file system, requests, and so on. Thank you for reading!

  • post-author-pic
    Terrence C
    11-22-2016

    Wow nice job Francisco!

  • post-author-pic
    Francisco S
    11-24-2016

    Thanks Terry, glad you like it!

Looking For Team Training?

Learn More