Introduction to Node.js & Express

What is Node.js

Traditionally, developers only used the JavaScript language in web browsers. But, in 2009, Node.js was unveiled, and with it, the developer tool kit expanded greatly. Node.js gave developers the chance to use JavaScript to write software that, up to that point, could only be written using C, C++, Java, Python, Ruby, C#, and the like.

We will use Node to write server code. Specifically, web services that communicate with clients using the JavaScript Object Notation (JSON) format for data interchange.

Advantages of Node.js

  • Node.js uses the same programming language (JavaScript) and paradigm for both client and server. Using the same language, we minimize context switching and share code between the client and the server.
  • JavaScript is single-threaded, which removes the complexity involved in handling multiple threads.
  • JavaScript is asynchronous, which allows us to take full advantage of the processor it's running on. Taking full advantage of the processor is crucial because the node process will be running on a single CPU.
  • Using JavaScript gives us access to the npm repository. This repository is the largest ecosystem of valuable libraries (most free to use) in npm modules.

Disadvantages of Node.js

  • By strictly using JavaScript on the server, we cannot use the right tool (a particular language) for the job.
  • Because JavaScript is single-threaded, we can't take advantage of servers with multiple cores/processors.
  • Because JavaScript is asynchronous, it is harder to learn for developers that have only worked with languages that default to synchronous operations that block the execution thread.
  • In the npm repository, there are often too many packages that do the same thing. This excess of packages makes it harder to choose one and, in some cases, may introduce vulnerabilities into our code.

Writing a Simple Web Server

To write a simple web server with Node.js:

  • Use Node's HTTP module to abstract away complex network-related operations.
  • Write the single request handler function to handle all requests to the server.

The request handler is a function that takes the request coming from the client and produces the response. The function takes two arguments: 1) an object representing the request and 2) an object representing the response.

This process works, but the resulting code is verbose, even for the simplest of servers. Also, note that when using only Node.js to build a server, we use a single request handler function for all requests.

How to Build It

1. First, create a new folder for your server project and create an index.js file inside it.

2. Add this code to index.js:

const http = require("http"); // built in node.js module to handle http traffic

const hostname = "127.0.0.1"; // the local computer where the server is running
const port = 3000; // a port we'll use to watch for traffic

const server = http.createServer((req, res) => {
    // creates our server
    res.statusCode = 200; // http status code returned to the client
    res.setHeader("Content-Type", "text/plain"); // inform the client that we'll be returning text
    res.end("Hello World from Node\n"); // end the request and send a response with the specified message
});

server.listen(port, hostname, () => {
    // start watching for connections on the port specified
    console.log(`Server running at http://${hostname}:${port}/`);
});

3. Open your terminal, navigate to your project folder, and run:

node index.js

4. You should see the message: "Server running at http://127.0.0.1:3000"

5. Open your browser and visit: http://localhost:3000

You should see the message "Hello World from Node" displayed in your browser. Congratulations! You've created your first web server using Node.js.

What is Express

Node's built-in HTTP module provides a powerful way to build web applications and services. However, it requires a lot of code for everyday tasks like sending an HTML page to the browser.

Introducing Express, a light and unopinionated framework that sits on top of Node.js, making it easier to create web applications and services. Sending an HTML file or image is now a one-line task with the sendFile helper method in Express.

Ultimately, Express is just a Node.js module like any other module.

What can we do with Express?

  • Build web applications.
  • Serve Single Page Applications (SPAs).
  • Build RESTful web services that work with JSON.
  • Serve static content, like HTML files, images, audio files, PDFs, and more.
  • Power real-time applications using technologies like Web Sockets or WebRTC.

Benefits of Express

  • Simple
  • Unopinionated
  • Extensible
  • Light-weight
  • Compatible with Connect middleware
  • Clean, intuitive, and easy-to-use API
  • Abstracts away common tasks

Drawbacks of Express

  • It's not a one-stop solution. Because of its simplicity, it does very little out of the box—especially when compared to frameworks like Ruby on Rails and Django.
  • We are forced to make more decisions due to the flexibility and control it provides.

Main Features of Express

Middleware

  • Middleware functions can get the request and response objects, operate on them, and (when specified) trigger some action. Examples are logging or security.
  • Express's middleware stack is an array of functions.
  • Middleware can change the request or response, but it doesn't have to.

Routing

Routing is a way to select which request handler function is executed. It does so based on the URL visited and the HTTP method used. Thus, routing provides a way to break an application into smaller parts.

Routers for Application Modularity

We can break up applications into routers. For example, we could have a router to serve our SPA and another router for our API. Each router can have its middleware and routing. This combination provides improved functionality.

Convenience Helpers

Express has many helpers that provide out-of-the-box functionality to make writing web applications and API servers easier.

A lot of those helpers are extension methods added to the request and response objects.

Examples from the Api Reference include: response.redirect(), response.status(), response.send(), and request.ip.

Views

Views provide a way to dynamically render HTML on the server and even generate it using other languages.

How to Build It

Let's write our first server using Express:

  1. Create a new file called server.js to host our server code.
  2. Type npm init -y to generate a package.json.
  3. Install the express npm module using: npm install express.
  4. Inside server.js add the following code:
const express = require('express'); // import the express package

const server = express(); // creates the server

// handle requests to the root of the api, the / route
server.get('/', (req, res) => {
  res.send('Hello from Express');
});

// watch for connections on port 9000
server.listen(9000, () =>
  console.log('Server running on http://localhost:9000')
);
  1. Run the server by typing: node server.js and visit http://localhost:9000 in the browser.

To stop the server, type Ctrl + c at the terminal window.

Create an API

In this overview, we're walking through the steps necessary to build a simple Web API that returns the string "Hello World" on every incoming GET request. The program should return the string every time a request comes into the root route ("/"). For now, you don't need to code along, just read through the steps.

To make things easier, we'll use an existing repository as the base for our API. Then, later in the week, as we learn more about Node.js and Express, we'll create an API from scratch.

To build our first API, we will:

  1. clone the node-express-mini repository to a folder on our computer.
  2. Navigate into the folder using cd.
  3. Use npm install to download all dependencies.
  4. Add a file called index.js at the folder's root, next to the package.json file.
  5. Open the index.js file using our favorite code editor.
  6. Add the following code:
// require the express npm module, needs to be added to the project using "npm install express"
const express = require('express');

// creates an express application using the express module
const server = express();

// configures our server to execute a function for every GET request to "/"
// the second argument passed to the .get() method is the "Route Handler Function"
// the route handler function will run on every GET request to "/"
server.get('/', (req, res) => {
  // express will pass the request and response objects to this function
  // the .send() on the response object can be used to send a response to the client
  res.send('Hello World');
});

// once the server is fully configured we can have it "listen" for connections on a particular "port"
// the callback function passed as the second argument will run once when the server starts
server.listen(8000, () => console.log('API running on port 8000'));

make sure to save your changes to index.js.

We are using the express npm module in our code, so we need to add it as a dependency to our project. To do this:

  1. Open a terminal/console/command prompt window and navigate to the root of our project.
  2. Add express to our package.json file by typing npm install express.

Now we're ready to test our API!

In the terminal, still at the root of our project:

  1. Type: npm run server to run our API. The message "Api running on port 8000" should appear on the terminal.
  2. Open a web browser and navigate to "http://localhost:8000".

There we have it, our first API!

A lot is going on in those few lines of code (only six lines if we remove the comments and white space). We will cover every piece of it in detail over the following modules, but here is a quick rundown of the most important concepts.

First, we used require() to import the express module and make it available to our application. require() is similar to the import keyword we have used before. The line const express = require('express'); is equivalent to import express from 'express'; if we were using ES2015 syntax.

The following line creates our Express application. The return of calling express() is an Express application that we can use to configure our server and, eventually, start listening for and responding to requests. Notice we use the word server, not API. An Express application is generic, which means we can use it to serve static content (HTML, CSS, audio, video, PDFs, and more). We can also use an Express application to serve dynamically generated web pages, build real-time communications servers, and more. Finally, we will use it statically to accept requests from clients and respond with data in JSON format.

An Express application publishes a set of methods we can use to configure functions. We are using the .get() method to set up a route handler function that will run on every GET request. As a part of this handler function, we specify the URL which will trigger the request. In this case, the URL is the site's root (represented by a /). There are also methods to handle the POST, PUT, and DELETE HTTP verbs.

The first two arguments passed by express to a route handler function are 1) an object representing the request and 2) an object representing the response. Express expands those objects with a set of useful properties and methods. Our example uses the .send() method of the response object to specify the data we will send to the client as the response body. You can call the first two arguments anything you want, but it is prevalent to see them dubbed req and res. We at BloomTech call them the homies as they always hang out together.

That's all the configuring we need to do for this first example. We'll see other ways of configuring our server as we go forward.

After configuring the server, it's time to turn it on. We use the .listen() method to monitor a port on the computer for any incoming connections and respond to those we have configured. Our server will only respond to GET requests made to the / route on port 8000.

That's it for our first Web API, and now it's time for you to follow along as we add a new endpoint to our server that returns JSON data!

How to Build It

Let's try returning JSON instead of just a simple string.

Please follow the steps outlined on the overview, but, to save time, copy and paste the content of index.js instead of typing it. Then run your API through a browser to make sure it works.

Now follow along as we code a new endpoint that returns an array of movie characters in JSON format.

The first step is to define a new route handler to respond to GET requests at the /hobbits endpoint:

server.get('/hobbits', (req, res) => {
  // route handler code here
});

Next, we define the return data that our endpoint will send back to the client. We do this inside the newly defined route handler function:

const hobbits = [
    { id: 1, name: 'Samwise Gamgee' },
    { id: 2, name: 'Frodo Baggins' },
];

Now we can return the hobbits array. We could use .send(hobbits) as we did for the string on the / endpoint, but this time we'll learn about two other useful methods we find in the response object:

res.status(200).json(hobbits);

We should provide as much useful information as possible to the clients using our API. One such piece of data is the HTTP status code that reflects the client's operation outcome. In this case, the client is trying to get a list of a particular resource, a hobbits list. Sending back a 200 OK status code communicates to the client that the operation was successful.

We will see other status codes as we continue to build new endpoints during this week. You can see a list by following this link to the documentation about HTTP Response Codes on the Mozilla Developer Network site.

We can use the .status() method of the response object to send any valid HTTP status code.

We are also chaining the .json() method of the response object. We do this to communicate to both the client making the request and the next developer working with this code that we intend to send the data in JSON format.

The complete code for index.js should now look like so:

const express = require('express');

const server = express();

server.get('/', (req, res) => {
  res.send('Hello World');
});
server.get('/hobbits', (req, res) => {
  const hobbits = [
    { id: 1, name: 'Samwise Gamgee' },
    { id: 2, name: 'Frodo Baggins' },
  ];
  res.status(200).json(hobbits);
});

server.listen(8000, () => console.log('API running on port 8000'));

Now we can visit http://localhost:8000/hobbits in our browser, and we should get back our JSON array.

If you are using the Google Chrome browser, this extension can format the JSON data in a more readable fashion.

Besides GET, there also exist POST (for creating), PUT (for updating or replacing) and DELETE actions we can ask the server to perform. Here is our Hobbits API, expanded to support full CRUD (Create, Read, Update and Delete):

const express = require('express');

const server = express();
server.use(express.json()); // !! IMPORTANT this teaches express to parse req.body

let id = 0
let getId = () => ++id // helper function to create auto-incrementing ids

let hobbits = [ // our fake hobbits database table
  { id: getId(), name: 'Samwise Gamgee' },
  { id: getId(), name: 'Frodo Baggins' },
];

server.get('/hobbits', (req, res) => { // GET ALL EXISTING HOBBITS
  res.status(200).json(hobbits); // 200 means "OK"
});
server.get('/hobbits/:id', (req, res) => { // GET EXISTING HOBBIT BY id
  // the desired id comes in the URL, and is found in `req.params.id`
  res.status(200).json(hobbits.find(hob => hob.id == req.params.id));
});
server.post('/hobbits', (req, res) => { // POST NEW HOBBIT
  // the desired name comes in the body, and is found in `req.body.name`
  hobbits.push({ id: getId(), name: req.body.name });
  res.status(201).json(hobbits); // 201 means "Created"
});
server.put('/hobbits/:id', (req, res) => { // PUT EXISTING HOBBIT
  // the id to update is in `req.params.id` and the desired name in `req.body.name`
  hobbits = hobbits.map(hob => hob.id == req.params.id
    ? { ...hob, name: req.body.name } : hob);
  res.status(200).json(hobbits);
});
server.delete('/hobbits/:id', (req, res) => { // DELETE EXISTING HOBBIT
  hobbits = hobbits.filter(hob => hob.id != req.params.id);
  res.status(200).json(hobbits);
});

server.listen(8000, () => console.log('API running on port 8000'));

Using Postman to Test Your API

Testing APIs is different from testing websites or web applications. A web browser is sufficient to test the latter, but we need to make POST/PUT/PATCH/DELETE requests for APIs and even modify the request headers.

For testing, we will use a tool called Postman. Postman and other similar tools allow full control when making requests. For example, we can easily change the HTTP Method used, add JSON data to the body, add form data, add headers, examine the response, and more.

In Postman's case, you can do a lot more. But, for now, we'll limit our discussion to the basics needed to test APIs by passing test data and examining whether we're getting the expected response.

We recommend installing the native application for your Operating System, however, VS Code has a Postman extension as well that can really speed up the process.

Module 1 Project: Intro to Node.js & Express

The module project contains advanced problems that will challenge and stretch your understanding of the module's content. The project has built-in tests for you to check your work, and the solution video is available in case you need help or want to see how we solved each challenge, but remember, there is always more than one way to solve a problem. Before reviewing the solution video, be sure to attempt the project and try solving the challenges yourself.

Instructions

The link below takes you to Bloom's code repository of the assignment. You'll need to fork the repo to your own GitHub account, and clone it down to your computer:

Starter Repo: Node API 1

  • Fork the repository,
  • clone it to your machine, and
  • open the README.md file in VSCode, where you will find instructions on completing this Project.
  • submit your completed project to the BloomTech Portal

Solution

Additional Resources