DEV Community

Cover image for Introduction to Node
Ash
Ash

Posted on • Edited on

Introduction to Node

Over the last several years JavaScript has redefined the landscape of the internet, becoming a ubiquitous and essential language. Much of that proliferation was the result of runtime environments that aimed to take JavaScript (or JS) outside the confines of the browser. Many projects tried to accomplish this goal, but none caught on quite like NodeJS did when it arrived in 2009.

With NodeJS, JavaScript can run both in the browser and the computer processor, allowing developers to build fully-functional single page applications that once were only possible with languages like Java, Ruby, C++, Python, and more.

In this series we'll be taking a look at the NodeJS environment, discussing important paradigms and patterns crucial to understanding it. Later we'll use Express and Knex on top of Node to build a RESTful API with CRUD capabilities.

👉 NodeJS is written in C, C++ and JavaScript, on top of the V8 JavaScript engine that powers JS in browsers like Google Chrome.


Intro to NodeJS

NodeJS, or Node for simplicity, is a runtime environment. This concept is hard to put into words, but I think of a runtime environment as a program like any other. When executed the program enters a "runtime state." In this active state it can execute other programs, all while having access to the computer's processor, RAM, and other resources.

What Node is to JavaScript, our physical world is to us - the place we exist, where we can perform functions, and have access to various resources.

Node is, logically, more intuitive if your background includes JS. By nature JS is an asynchronous language, and Node is built on a callback based system to accommodate that.

The choice to use any one environment, framework or library comes with the responsibility to weigh both the advantages and disadvantages. What makes a program a solid solution on one team, can be the same things that make it an obstacle on another.

Let's look at some of the features of Node that make it a solution, and why those very same features can also be roadblocks.

✅ Pros:

  • Same Language: Using JS on both the server and the client means we don't have to adapt to paradigm and syntax shifts, reducing complexity and confusion for developers.

  • Asynchronous: Takes full advantage of JavaScript's processor - supporting asynchronous operations to read/write, connect to database servers, handle user requests, etc.

  • Single-Threaded: This isn't strictly true - the Node program, and its imported modules, will run on a single thread, while the engine and IO run on another. Either way, it reduces the complexity behind multiple threads in languages like Java.

  • NPM: Access to the npm registry - a huge library of npm modules, that often make developer-life easier.

📛 Cons:

  • Same Language: In programming we have various languages because each of them do one thing really well. Like the tools in a toolbox, they're all useful given the right job. Limiting ourselves to JS means using a tool that might not be right for the job.

  • Asynchronous: If you, or your team, are unfamiliar with handling the wonky async activity of JS, there can be a steep learning curve. Especially those coming from languages that are built on a synchronous approach to operations.

  • Single-Threaded: This comes back to the concept of the "right tool for the job." We lose the advantages of servers with multiple threads.

  • NPM: The npm registry is big, perhaps too big. Using it has the potential to open a project up to vulnerabilities or future deprecations from poorly maintained libraries.

We're going to build a simple server to discuss the fundamentals of Node further, but for now it's important to understand that Node is an environment that allows JS to run outside of the browser.


Installing Node

To continue, make sure that Node is installed on your machine and it's up-to-date. To check, open a terminal window and provide the following command:

node -v

If Node is installed this command will return the current version available. If you don't have Node, or its current version, head to the official NodeJS site and download it from there.

❓ Confused about what version to download? You'll find two versions of Node available on the official site, an "LTS" and a "Current" version. LTS stands for "long-term support", meaning it's being actively maintained and upgraded - but experimental features that don't see full support are not included. For our purposes, we're going to want the LTS version of Node.


Creating a Server

In the context of Node, a server is a place in a computer that listens for traffic. That traffic comes in the form of a user visiting a URL.

When a user visits a website, they're making a request for a resource. It's up to the server to take that request, and decide what resource should be sent back. We as developers will be responsible for writing the code that gives a server the ability to make that decision.

In a directory of your choosing create a new folder for this example project, I've named mine node_server_ex. Inside that folder create an index.js file.

Inside index.js we only need to add these few lines and we're ready to get off the ground.

// index.js

// 1️⃣
const http = require('http'); 
const host = `127.0.0.1`; 
const port = 3000; 

// 2️⃣
const server = http.createServer((req, res) => {
    res.statusCode = 200; 
    res.setHeader("Content-TYPE", "text/plain"); 
    res.end("Hello World, from Node!"); 
}); 

// 3️⃣
server.listen(port, host, () => {
    console.log(`server listening on http://${host}:${port}`);
}); 
Enter fullscreen mode Exit fullscreen mode

Let's step through the code line-by-line and learn how JS and Node work together.


1️⃣ Variables http, host and port

A server helps computers communicate - Node accomplishes communication using a built-in module called the HTTP Module. The http module is expansive, offering a suite of available properties, classes and their bound methods.

👉 If you're new to programming and are unclear about what a module is - a module is an object that contains a whole bunch of pre-built functions and classes we can choose to use. Pre-made functionality for the low-low price of free.

To include the http module we need to use the require syntax.

const http = require('http'); 
Enter fullscreen mode Exit fullscreen mode

You may be used to importing modules in JS using the import MODULE from PACKAGE syntax - this is the same thing, though more antiquated. Import syntax happens to be an ES6 feature and because Node runs on the V8 JavaScript engine, which supports new features like ES6, the import syntax will work for newer modules but will not for the older, core ones.

Providing the host and port variables allows us to set up constants that tell Node where to run, and where to listen. For the time being host directs Node to run on your local machine, and port is a number indicating the communication endpoint between the server and user.


2️⃣ createServer Function

After requiring the http module, we first put it to use by creating an instance of a Server class. We know that a class in programming is an object built for a specific purpose - equipped with data and behavior presets designed to fulfill that purpose.

If we had to build our own server class we would find it a monumental task - there's a lot to consider, and huge room for error. Thankfully for us, the talented and knowledgeable developers at Node did that for us and made it available to use on the http module.

To create an instance of a Node server class, we simply call http, and the .createServer() method, storing it in local variable server:

const server = http.createServer((req, res) => {
        res.statusCode = 200; 
    res.setHeader("Content-TYPE", "tex/plain"); 
    res.end("Hello World, from Node!"); 
}); 
Enter fullscreen mode Exit fullscreen mode

The .createServer() method takes a callback function that passes two parameters - the request object and the response object, req and res.

As their names imply, these objects contain information about a user request, and the server's response to that request. They can contain hundreds of properties and complex data structures, or they can be nearly empty, sending simple pieces of information.

📝 req and res are just implicit names we picked, they can be called anything, but they always map to the http.IncomingMessage and http.ServerResponse classes respectively. These classes were created as a result of instantiating a Server class with http.createServer().

In this example we're using the response object, and some its Node-provided methods, to send something to the user. The .statusCode() method sets the http status code to the "all clear!" code of 200, indicating a successful connection.

There are five classes of status codes, each intended to relay a different message. Using them correctly should be given special attention.

  • 1XX: Informational - The "We're all fine here now, thank you. How are you?" of status codes.
  • 2XX: Success - The "happy path." These are the status codes indicate that the server and client are communicating as expected.
  • 3XX: Redirection - If a server needs to pass off a request for further action before it can be completed, then a 3XX status code is used.
  • 4XX: Client Error - These errors point to a problem with the client - it could be an incorrect URL, unauthorized request, and so on.
  • 5XX: Server Error - 5XX errors tell us that the client is successfully sending a request, but the server is unable to handle it for some reason.

👉 We won't see much of the 1XX or 3XX codes, but using the correct 2XX, 4XX and 5XX codes will be important later!

In a perfect world the user won't be aware of these status codes, they're quietly hidden away in component of the request and response object known as the headers object. For developers, the headers are like a reference - a place we can know to look for more information about a request or response - like the date and time, authorization and server status, along with other useful operating parameters.

In the .setHeader() method we added an additional operating parameter to the response headers that will be tacked on with the status code we set above it. That parameter defines the "Content-TYPE" of the response to be "text/plain". This lets the browser know what type of data it will be receiving, so it can prepare itself to display that while the rest of the response loads asynchronously.

Maintaining well-informed headers only works to serve yourself and your fellow developers. Headers encourage meaningful communication between the front and backend, reduce errors and bugs, find and diagnose those bugs more quickly when they do arise, and improve performance.

Finally, the http transaction is terminated with the .end() method. This method ends the server function, passing in the resource that user originally asked for. That resource will be added to the response object along with the headers we constructed, where it will usually be consumed by the frontend in some way.


3️⃣ listen Method

Event handlers are a core feature of most programming languages, adding interactive features to a program through binding actions (what are commonly called "listeners") to elements. Or, as is the case in our example, to a port - a place in a computer.

In JS, we're familiar with binding an action to an element, like a button, using .addEventListener(). When that button is later clicked (acted upon), something will happen. In Node, using the .listen() method is the loose equivalent. Loose because despite both using JS, the event model in the browser environment and the event model in the Node environment are not identical.

This step is akin to turning the server on - we'll pass 3 of 4 optional arguments so that the server first knows where it will receive traffic, and second, will give us a message when the event listener is successfully bound - that is, it's listening!

server.listen(port, host, () => {
    console.log(`server listening on http://${host}:${port}`);
}
Enter fullscreen mode Exit fullscreen mode

The .listen() method comes to us on the Server class of the http module. It takes up to four arguments - a port, a host, a backlog and a callback function. This callback function is kind of like a "listener for the listener." It only fires when the listener is successfully bound, meaning that the server was able to start watching for traffic at the port we gave it.

📝 About Developer Style

In our example the .listen() method is called on its own line but you'll see this done differently, with other arguments passed, by other developers. Code is pliable, and methods are usually chainable. For example, we could have omitted the third argument and attached the event listener to the tail of the createServer function.

const server = http.createServer((req, res) => {
    res.statusCode = 200; 
    res.setHeader("Content-TYPE", "tex/plain"); 
    res.end("Hello World, from Node!"); 
}).listen(port, host); 
Enter fullscreen mode Exit fullscreen mode

Both examples achieve the same result - letting the server know that we want it to start listening at port 3000 on our machine. The subtle differences in approach come down to developer style. Personally, I find the file easier to read from top to bottom when major actions, like turning the server on, get their own space in the document flow. Pay attention to these differences, reasoning about them and trying them out in your own projects can help you get better at reading code and develop your coding style.


Interacting the Server

With our simple server built, we'll see if it works by executing it inside of the Node environment. To do that make sure the index.js file is saved and open a terminal window.

From there we need to navigate to the directory the server can be found in. The cd command stands for "change directory", use it to move through the directories and files of your machine. You can chain further directions by using a forward-slash, /.

I've stored this project in a folder named node_server_ex, located on my desktop. I can navigate to it like so:

cd desktop/node_server_ex

Once in the correct directory, use the "list files" command, ls, to make sure index.js is there.

ls

index.js

Now we call Node and tell it to run the server by giving its file name.

node index.js

Assuming everything goes as planned and there are no errors, the Node environment will execute the file, going through the code inside, turning on the server's event listener, and finally returning the message found in the listen method's callback function: server listening on http://127.0.0.1:3000

The terminal window is now devoted to propping the Node environment so the server can continue running, waiting for user traffic. We can't use this particular window for anything else right now - if you try to navigate to a different directory, or use the ls command, nothing will happen. But we need to keep it open or the Node environment will close, taking the server with it.

With the server running, we now just need to visit its port so we can see what it's sending! Open a browser window and navigate to http://localhost:3000 - where you should see your server's welcome message - "Hello World, from Node!".


With that, you've built a Node server! It can't do much right now, but this example helps conceptualize some of the fundamentals of Node. We learned that inside the Node environment we can use JS outside of the browser - on the backend - to process user requests, send responses and provide the functionality of a full-featured computer application.

Much of the work done in Node relies on its communication-obsessed http module, which gives us a robust Server class and a myriad of methods to receive and handle and manipulate requests, send detailed headers and more.

In later installments of this series on Node and Express we'll discuss more about the asynchronous nature of Node and JS, cover some database fundamentals, and see how Node, Express and JS can be used to perform CRUD operations and build RESTful APIs.


Resources:

I hope you enjoyed the first part of this series - if I got anything wrong, didn't explain it enough, or missed something important - please reach out and let me know! All feedback is welcome, and thank you for reading 🦄


Errors and Updates:

Update 3/10/21: I updated the section on http headers. I was erroneously referring to the "header" object, when I should have been referring to the plural "headers." This was a misstep I became aware of as I prepare to share the next part in the series.

Top comments (0)