Before starting with Express.js, we must ensure we have a solid grasp of Node.js. Since Express builds on top of Node, understanding its runtime environment, module system, and core capabilities will make learning Express much smoother.

Let’s dive in!

Understanding the Node.js runtime

Node.js is a JavaScript runtime that lets you run JavaScript on servers, command line tools, and other environments outside the browser. It uses Chrome’s fast V8 engine under the hood to execute JavaScript. Unlike a browser environment, Node.js provides direct access to the operating system, file system, and network, making it a powerful tool for back-end development.

To handle multiple tasks efficiently, Node.js adopts a single-threaded, event-driven model that employs non-blocking I/O. This design choice allows it to process many requests concurrently without waiting for previous ones to complete.

Working with modules and npm

Node.js organizes code using modules, which help keep projects modular and maintainable. There are three types of modules:

  • Built-in modules: Predefined modules like fspath, and http

  • Custom modules: Our reusable code files

  • Third-party modules: Packages installed via npm

Using a built-in module

Built-in Node.js modules provide functionality for various tasks. For example, the file system module fs allows us to perform file operations, as shown below. These modules come preinstalled with Node.js, so we can use them directly without installing additional packages.

Press + to interact
index.js
example.txt
const fs = require('fs');
const data = fs.readFileSync('example.txt', 'utf8');
console.log(data);

Explanation:

  • Line 1: This imports the fs module, which provides file system operations.

  • Line 3: This reads a file synchronously using fs.readFileSync, meaning execution pauses until the file is fully read. The 'utf8' argument specifies that the file should be interpreted as UTF-8 encoded text, which is the standard encoding for most text files.

Creating and using a custom module

Custom modules can be defined in separate .js files within a project and imported wherever needed. In Node.js, a file becomes a module when it exports functionality that other files can use. To better understand how they work, let’s create a simple module that returns a greeting message. 

Press + to interact
index.js
greet.js
const defaultGreeting = "Hello, there!";
function greet(name) {
return `Hello, ${name}!`;
}
module.exports = { greet, defaultGreeting };

Explanation:

  • In greet.js:

    • Lines 1–3: These define defaultGreeting, a reusable constant, and greet(name), a function that returns a personalized greeting.

    • Line 7: This exports both greet and defaultGreeting using module.exports, allowing them to be imported into other files.

  • In index.js:

    • Line 1: Imports both the greet function and defaultGreeting from greet.js.

    • Lines 3–4: Invoke the greet function and log the output. Also, logs defaultGreeting to show that it can be accessed independently.

Third-party package management with npm

Instead of writing all functionality from scratch, we can use third-party packages. Node.js comes with npm (Node Package Manager), which helps install and manage third-party packages. Every project that uses npm has a package.json file, which keeps track of dependencies.

The package.json file stores metadata about a Node.js project, including dependencies and scripts. This file ensures that the correct package versions are installed when sharing the project with others.

{
"name": "my-project",
"version": "1.0.0",
"description": "A sample project",
"main": "index.js",
"type": "module",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
An example of the package.json file

Here are some of the most commonly used fields in package.json:

name

Project name

version

Project version

type

Defines module type (module for ESM, default is CommonJS—we’ll see these shortly)

scripts

Custom commands (e.g., npm start)

dependencies

Installed packages

Installing and using third-party packages

Use the following command to install a third-party package (like lodash):

npm install lodash

Follow the commands below to use it in the code:

Press + to interact
const _ = require('lodash');
console.log(_.capitalize('hello'));

Explanation:

  • Line 1: This imports the lodash library, a popular JavaScript utility library.

  • Line 2: This uses _.capitalize() to capitalize the first letter of the string ‘hello’, demonstrating how lodash simplifies common string operations.

Note: If package.json does not exist, lodash will still install and function, but it won’t be recorded anywhere for future installs. This means that if another developer clones the project, they won’t automatically get lodash unless package.json is present.

CommonJS vs. ES Modules in Node.js

Node.js supports two module systems: CommonJS (CJS) and ES Modules (ESM). Historically, CommonJS has been the default module system in Node.js. It uses require() to import modules and module.exports to export them. This approach remains well-suited for server-side development and is still widely used in many Node.js projects.

In contrast, ES Modules use import and export, and they load asynchronously, meaning they do not block code execution during imports. They also work natively in browsers and are fully supported in newer versions of Node.js.

Here’s a simple example demonstrating how to use import and export for modularizing code:

Press + to interact
index.mjs
package.json
greet.mjs
export const defaultGreeting = "Hello, there!";
export function greet(name) {
return `Hello, ${name}!`;
};

Explanation:

  • greet.mjs: We use the export keyword to make functions, variables, or objects available for use in other files.

  • index.mjs: We import both the greet function and defaultGreeting variable from greet.mjs.

To use ES Modules in a Node.js project, we need to specify "type": "module" in the package.json file. This setting tells Node.js to interpret files using ES module syntax instead of CommonJS.

Side-by-side comparison

Here’s a quick comparison of the two module systems:

Feature

CommonJS (CJS)

ES Module (ESM)

Default in Node.js?

Yes

No (must enable with "type": "module" in package.json)

Works in Browsers?

No

Yes

Execution Style

Synchronous

Asynchronous

Usage in Node.js

Older codebases, compatibility with existing tools

Recommended for modern applications

Creating a simple HTTP server

Now that we have covered how Node.js handles modules and package management, let’s explore how we can use Node.js to create a basic web server. Node.js provides the http module for handling HTTP requests.

{
  "name": "my-project",
  "version": "1.0.0",
  "description": "A sample project",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.18.2"
  }
}
Basic HTTP server example

Explanation:

  • Line 1: This imports the built-in http module.

  • Line 3: This creates an HTTP server that listens for requests.

  • Line 4: This sets the response status code to 200, indicating that the request was successful. The content type is set to text/plain to specify that the response body contains plain text rather than HTML or JSON.

  • Line 5: This sends back the response “Hello, World!”.

  • Line 8: The server listens on port 3000.

While this works, managing multiple routes with the http module becomes cumbersome. Express.js simplifies this process significantly.

Key takeaways:

  • Node.js is a JavaScript runtime environment that executes code outside the browser using the V8 engine.

  • It follows a single-threaded, event-driven architecture with non-blocking I/O.

  • The module system in Node.js allows us to use built-in modules, third-party packages, and custom modules.

  • The http module enables us to create a basic web server, but it has limitations.

Exercise: Working with modules in Node.js

The following files have been pre-created for you: mathOperations.jsindex.js, and package.json. Your task is to complete the missing code in these files.

Task 1:

In mathOperations.js, the following functions have been defined for you. Your task is to export them so they can be used in index.js:

    1. add(a, b): This returns the sum of two numbers.

    2. multiply(a, b): This returns the product of two numbers.

Task 2:

In index.js:

    1. Import the functions from mathOperations.js.

    2. Use them to perform calculations.

    3. Print the results of calling both functions.

Run index.js to verify the output.

Press + to interact
index.js
mathOperations.js
// Task 1: Export the following functions...
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}