The Node.js and Express.js Handbook for Beginners – Servers, Routes, Routers, and Views Explained — Opportunihub
Course Remote

The Node.js and Express.js Handbook for Beginners – Servers, Routes, Routers, and Views Explained

Oluwatobi Sofela · Remote

At a glance

Type
Course
Organisation
Oluwatobi Sofela
Location
Remote
Work mode
Remote
Deadline
Rolling / not stated
Posted
1 Oct 2026

About this course

<p>Node.js is a runtime environment for executing JavaScript outside the web browser. From small scripts to large-scale back-end applications, Node.js provides the APIs and tools you need to build JavaScript applications for a variety of environments.</p> <p>But learning Node.js can feel overwhelming. With so many APIs, packages, tools, and concepts to understand, it’s easy to feel lost.</p> <p>That’s why this book focuses on the fundamental Node.js concepts you need to build practical applications without unnecessary distractions. You’ll learn how Node.js works, how to use its built-in APIs, and how to create applications that interact with files, URLs, events, HTTP requests, and more.</p> <p>You’ll also learn how Express.js simplifies many common server-side tasks. We’ll explore routes, routers, controllers, views, and other concepts that help you organize and build web applications more efficiently.</p> <p>Whether you’re learning backend development for the first time or expanding your JavaScript skills beyond the browser, this guide is designed to give you a clear and practical foundation for building with Node.js and Express.js.</p> <h2 id="heading-table-of-contents">Table of Contents</h2> <ul> <li><p><a href="#heading-what-you-should-already-know">What You Should Already Know</a></p> </li> <li><p><a href="#heading-the-tools-youll-need">The Tools You'll Need</a></p> </li> <li><p><a href="#heading-what-exactly-is-nodejs">What Exactly Is Node.js?</a></p> <ul> <li><p><a href="#heading-create-a-new-directory-for-your-project">Create a New Directory for Your Project</a></p> </li> <li><p><a href="#heading-create-a-packagejson-file">Create apackage.jsonFile</a></p> </li> </ul> </li> <li><p><a href="#heading-why-nodejs">Why Node.js?</a></p> <ul> <li><p><a href="#heading-1-create-a-javascript-file">1. Create a JavaScript File</a></p> </li> <li><p><a href="#heading-2-write-your-javascript-program">2. Write your JavaScript Program</a></p> </li> <li><p><a href="#heading-3-run-your-javascript-program">3. Run your JavaScript Program</a></p> </li> <li><p><a href="#heading-4-automatically-rerun-your-javascript-program">4. Automatically Rerun Your JavaScript Program</a></p> </li> </ul> </li> <li><p><a href="#heading-nodes-module-support">Node's Module Support</a></p> </li> <li><p><a href="#heading-create-http-servers-with-nodejs">Create HTTP Servers with Node.js</a></p> <ul> <li><p><a href="#heading-initialize-a-new-instance-of-the-http-server-object">Initialize a New Instance of the HTTPServerObject</a></p> </li> <li><p><a href="#heading-configure-the-systems-port-where-the-server-should-run">Configure the System's Port Where the Server Should Run</a></p> </li> <li><p><a href="#heading-configure-the-server-to-respond-to-client-requests">Configure the Server to Respond to Client Requests</a></p> </li> </ul> </li> <li><p><a href="#heading-what-exactly-is-expressjs">What Exactly Is Express.js?</a></p> <ul> <li><p><a href="#heading-1-install-express">1. Install Express</a></p> </li> <li><p><a href="#heading-2-use-expressjs-to-configure-the-projects-nodejs-web-server">2. Use Express.js to Configure the Project's Node.js Web Server</a></p> </li> <li><p><a href="#heading-3-run-the-expressjs-web-server">3. Run the Express.js Web Server</a></p> </li> </ul> </li> <li><p><a href="#heading-raw-nodejs-code-vs-expressjs-code">Raw Node.js Code vs. Express.js Code</a></p> <ul> <li><p><a href="#heading-example-1-use-raw-nodejs-to-handle-web-requests">Example 1: Use raw Node.js to Handle Web Requests</a></p> </li> <li><p><a href="#heading-example-2-use-expressjs-to-handle-web-requests">Example 2: Use Express.js to Handle Web Requests</a></p> </li> </ul> </li> <li><p><a href="#heading-important-stuff-to-know-about-creating-expressjs-web-servers-in-nodejs">Important Stuff to Know About Creating Express.js Web Servers in Node.js</a></p> <ul> <li><p><a href="#heading-its-common-to-call-applisten-last">It's Common to Callapp.listen()Last</a></p> </li> <li><p><a href="#heading-you-can-set-the-port-number-using-environment-variables">You Can Set the Port Number Using Environment Variables</a></p> </li> <li><p><a href="#heading-common-expressjs-response-methods">Common Express.js Response Methods</a></p> </li> <li><p><a href="#heading-expresss-response-methods-do-not-terminate-the-http-request-handlers-execution">Express's Response Methods Do Not Terminate the HTTP Request Handler's Execution</a></p> </li> </ul> </li> <li><p><a href="#heading-what-is-a-route-in-expressjs">What Is a Route in Express.js?</a></p> <ul> <li><p><a href="#heading-syntax-of-a-route-in-express">Syntax of a Route in Express</a></p> </li> <li><p><a href="#heading-middleware-vs-route-handler">Middleware vs. Route Handler</a></p> </li> <li><p><a href="#heading-route-examples-in-expressjs">Route Examples in Express.js</a></p> </li> <li><p><a href="#heading-important-things-to-know-about-the-next-parameter">Important Things to Know About thenextParameter</a></p> </li> <li><p><a href="#heading-categories-of-middleware-in-expressjs">Categories of Middleware in Express.js</a></p> </li> </ul> </li> <li><p><a href="#heading-what-is-the-expressrouter-method">What Is the express.Router() Method?</a></p> <ul> <li><p><a href="#heading-create-a-directory-for-the-routers">Create a Directory for the Routers</a></p> </li> <li><p><a href="#heading-create-the-route-files">Create the Route Files</a></p> </li> <li><p><a href="#heading-create-a-mini-app-for-the-book-related-routes-and-middleware">Create a Mini-App for the Book-Related Routes and Middleware</a></p> </li> <li><p><a href="#heading-time-to-practice-with-routers">Time to Practice with Routers</a></p> </li> <li><p><a href="#heading-update-the-main-express-application">Update the Main Express Application</a></p> </li> <li><p><a href="#heading-run-your-expressjs-application">Run your Express.js Application</a></p> </li> <li><p><a href="#heading-important-things-to-know-about-express-routers">Important Things to Know About Express Routers</a></p> </li> </ul> </li> <li><p><a href="#heading-what-is-a-view-in-expressjs">What Is a View in Express.js?</a></p> <ul> <li><p><a href="#heading-what-are-static-views-in-expressjs">What Are Static Views in Express.js?</a></p> </li> <li><p><a href="#heading-what-are-dynamic-views-in-expressjs">What Are Dynamic Views in Express.js?</a></p> </li> </ul> </li> <li><p><a href="#heading-overview">Overview</a></p> <ul> <li><a href="#heading-dive-deeper-into-nodejs-and-expressjs">Dive Deeper into Node.js and Express.js</a></li> </ul> </li> </ul> <h2 id="heading-what-you-should-already-know">What You Should Already Know</h2> <p>This guide is for readers who know basic JavaScript and want to start building web applications with Node.js and Express.js. You'll get the most value from it if you're familiar with:</p> <ul> <li><p>JavaScript fundamentals, including variables, functions, arrays, and objects.</p> </li> <li><p>Basic asynchronous JavaScript, including callbacks, promises, and <code>async</code>/<code>await</code>.</p> </li> <li><p>Basic HTML, including links, forms, and input elements.</p> </li> <li><p>Using a terminal to navigate folders and run commands.</p> </li> </ul> <p>You don't need previous experience with Node.js or Express.js. Familiarity with installing npm packages is helpful, but the examples will guide you through the commands you need.</p> <h2 id="heading-the-tools-youll-need">The Tools You'll Need</h2> <p>Before you begin, have a code editor, a web browser, and the following installed:</p> <ul> <li><p>Node.js 24.15.0 or later.</p> </li> <li><p>npm 11.14.0 or later.</p> </li> </ul> <p>Check your installed versions by running:</p> <pre><code class="language-console">node --version &amp;&amp; npm --version </code></pre> <p>Node.js includes npm, but the bundled version may be older than the one listed here. Check both versions and update npm separately if needed. The <a href="https://codesweetly.com/package-manager-explained">CodeSweetly package manager guide</a> explains how to install, update, and check these tools.</p> <p>The file-creation commands assume an environment with <code>mkdir</code> and <code>touch</code> available, such as Bash or zsh on macOS or Linux, Git Bash on Windows, or a Linux shell through Windows Subsystem for Linux (WSL). You can also create the files and folders directly in your editor.</p> <p>Let's get started with Node.js.</p> <h2 id="heading-what-exactly-is-nodejs">What Exactly Is Node.js?</h2> <p>Node.js is not a framework or language. Instead, it is an <a href="https://codesweetly.com/asynchronous-javascript">asynchronous</a>, <a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Events">event</a>-driven environment that includes tools such as:</p> <ul> <li><p>JavaScript engine (Google's V8)</p> </li> <li><p>Module system (CommonJS and ES modules)</p> </li> <li><p>Operating system APIs (<code>os</code>)</p> </li> <li><p>Filesystem access (<code>fs</code>)</p> </li> <li><p>Network servers (<code>http</code>, <code>net</code>, <code>https</code>)</p> </li> <li><p>Memory management tools</p> </li> <li><p>Event loop</p> </li> </ul> <p>These components make up Node.js's core infrastructure, enabling JavaScript to run outside of browsers.</p> <img src="https://cdn.hashnode.com/uploads/covers/61672d1653401f641ba159b4/2ed752ef-64a7-43a8-9323-53ee00665048.jpg" alt="Node.js explained" style="display: block;" width="600" height="400" loading="lazy"> <p>The image above illustrates Node.js as a well-equipped runtime environment providing the infrastructure to build and run any scale of JavaScript applications outside the browser, including servers, CLIs, scripts, and full-stack applications.</p> <p>Let's discuss the points highlighted in the image by building applications with Node.js and Express.js. To begin, create the project directory.</p> <h3 id="heading-create-a-new-directory-for-your-project">Create a New Directory for Your Project</h3> <p>Use the <code>mkdir</code> CLI command to create a new project directory as follows:</p> <pre><code class="language-console">mkdir codesweetly-nodejs-app-001 </code></pre> <p><strong>Note:</strong> You can use any name you prefer. In this guide, we'll use <code>codesweetly-nodejs-app-001</code> for demonstration.</p> <p>Afterward, navigate to your project directory using the command line.</p> <pre><code class="language-console">cd codesweetly-nodejs-app-001 </code></pre> <h3 id="heading-create-a-packagejson-file">Create a <code>package.json</code> File</h3> <p>Use npm to initialize a <a href="https://codesweetly.com/package-json-file-explained"><code>package.json</code> file</a> after navigating into the project directory.</p> <pre><code class="language-console">npm init -y </code></pre> <p>Next, use the following command to delete the optional <code>main</code> field from <code>package.json</code>:</p> <pre><code class="language-console">npm pkg delete main </code></pre> <p><strong>Tip:</strong> The <code>main</code> field in <code>package.json</code> identifies a package's entry point when another program loads the package. We do not need it for this project because we'll run our scripts directly. Leaving it in place is also harmless.</p> <h2 id="heading-why-nodejs">Why Node.js?</h2> <p>JavaScript was originally created for web browsers, but other environments also provide the infrastructure to execute it.</p> <p>Node's 2009 release provided an environment for running JavaScript outside the web browser. For example, let's create a script to run from our system's terminal.</p> <h3 id="heading-1-create-a-javascript-file">1. Create a JavaScript File</h3> <p>Create the JavaScript file you want Node to run.</p> <p>In a shell that supports <code>touch</code>, such as Bash or zsh, use the following command. Otherwise, create the file in your code editor:</p> <pre><code class="language-console">touch console.js </code></pre> <h3 id="heading-2-write-your-javascript-program">2. Write your JavaScript Program</h3> <p>Open the newly created JavaScript file and write your program:</p> <pre><code class="language-javascript">console.log("=== Hello from the CodeSweetly Team! ==="); console.log("We hope you have fun Coding Sweetly with Node.js."); console.log("Thank you for being part of the CodeSweetly community."); console.log("=== Keep coding. Keep creating. Keep shipping. ==="); </code></pre> <h3 id="heading-3-run-your-javascript-program">3. Run your JavaScript Program</h3> <p>Installing Node.js makes the <code>node</code> command available for running a Node application from your command line.</p> <pre><code class="language-console">node console.js </code></pre> <ul> <li><p><code>node</code>: The command for running Node.js scripts or the REPL.</p> </li> <li><p><code>console.js</code>: The JavaScript file you want Node to run.</p> </li> </ul> <p>You can also add the command to the <code>"scripts"</code> field of your project's <code>package.json</code> file:</p> <pre><code class="language-json">{ "scripts": { "start": "node console.js", "test": "echo \"Error: no test specified\" &amp;&amp; exit 1" } } </code></pre> <p>With this script in place, you can run your JavaScript program from your terminal like this:</p> <pre><code class="language-console">npm run start </code></pre> <p>Once you execute your script, Node will print the file's output to your terminal. It will look like this:</p> <pre><code class="language-console">$ npm run start &gt; codesweetly-nodejs-app-001@1.0.0 start &gt; node console.js === Hello from the CodeSweetly Team! === We hope you have fun Coding Sweetly with Node.js. Thank you for being part of the CodeSweetly community. === Keep coding. Keep creating. Keep shipping. === </code></pre> <p>As you can see, we've successfully executed JavaScript outside a web browser. That's precisely what Node.js helps us with. It is an asynchronous, event-driven runtime environment for running JavaScript code outside the web browser. You can also automate rerunning your program. Let's discuss how.</p> <h3 id="heading-4-automatically-rerun-your-javascript-program">4. Automatically Rerun Your JavaScript Program</h3> <p>By default, Node.js requires you to manually rerun your JavaScript file each time you make changes.</p> <p>Repeating the manual process of executing your application as you make changes can be burdensome. Luckily, Node provides the <code>--watch</code> flag for automating the process:</p> <pre><code class="language-console">node --watch filename.extension </code></pre> <p>The snippet above uses the <code>--watch</code> flag to start Node.js in watch mode. This causes Node to re-execute the specified script whenever you update any of the files in its dependency graph.</p> <ul> <li><p><code>node</code>: The command for running Node.js scripts or the REPL.</p> </li> <li><p><code>--watch</code>: The flag for activating Node's watch mode.</p> </li> <li><p><code>filename.extension</code>: The script you want Node to execute.</p> </li> </ul> <p><strong>Tip:</strong> Stop the running process using <code>Ctrl + C</code> on Windows, macOS, or Linux.</p> <p>The <code>--watch</code> flag, by default, watches the entry point and all the modules it depends on. In other words, the watch command causes Node to monitor:</p> <ul> <li><p>The entry file (<code>filename.extension</code>)</p> </li> <li><p>All files imported by the entry file</p> </li> <li><p>All files imported in those imports</p> </li> <li><p>And so on through the whole dependency graph</p> </li> </ul> <p>Node's default watch mode does not watch unrelated files. If you want Node to watch other files, use the <code>--watch-path</code> flag:</p> <pre><code class="language-console">node --watch-path=./src --watch-path=./tests filename.extension </code></pre> <p>The <code>--watch-path</code> flag, in the snippet above, tells Node to watch all files in the <code>src</code> and <code>tests</code> directories, even if they are not in the entry point's dependency graph.</p> <ul> <li><p><code>node</code>: The command for running Node.js scripts or the REPL.</p> </li> <li><p><code>--watch-path</code>: The flag for specifying the path you want Node to watch for changes.</p> </li> <li><p><code>filename.extension</code>: The script you want Node to execute.</p> </li> </ul> <p><strong>Tip:</strong></p> <ul> <li><p>The <code>--watch-path</code> flag causes Node to ignore the dependency graph's modules unless they are part of <code>--watch-path</code>. Instead, Node.js will watch only the exact paths you specified, and nothing else.</p> </li> <li><p><code>--watch-path</code> enables watch mode itself. If you also pass <code>--watch</code>, Node still watches only the specified paths.</p> </li> <li><p><code>--watch-path</code> is supported on macOS and Windows, but not Linux. On Linux, use <code>--watch</code> to monitor the entry file and its dependencies.</p> </li> <li><p>Watch commands do not reload the browser automatically. They simply detect file changes, so Node reruns the program you specified. As such, if your program sends data to browsers, you will need to refresh the browser yourself or use a third-party tool for auto-reloading.</p> </li> </ul> <p>Node.js supports two module systems. Let's learn about them.</p> <h2 id="heading-nodes-module-support">Node's Module Support</h2> <p>Node.js supports both CommonJS (<code>.cjs</code>) and ECMAScript (<code>.mjs</code>) <a href="https://codesweetly.com/javascript-modules-tutorial">modules</a>. This allows you to use your preferred module type to create Node.js applications.</p> <p>For example, below is a <code>script.mjs</code> JavaScript file. Node will treat it as an ECMAScript module because it has a <code>.mjs</code> file extension.</p> <p><code>script.mjs</code></p> <pre><code class="language-javascript">import http from "node:http"; </code></pre> <p>On the other hand, Node will regard the <code>script.cjs</code> JavaScript file below as a CommonJS module because it has a <code>.cjs</code> file extension.</p> <p><code>script.cjs</code></p> <pre><code class="language-javascript">const http = require("node:http"); </code></pre> <p>Suppose you want to specify the module type for the <code>.js</code> files in your project. In that case, specify a <code>type</code> field in your <code>package.json</code> file like so:</p> <p><code>package.json</code></p> <pre><code class="language-json">{ "scripts": { "start": "node console.js", "test": "echo \"Error: no test specified\" &amp;&amp; exit 1" }, "type": "module", "license": "ISC" } </code></pre> <p>The <code>"type": "module"</code> field in the snippet above makes Node treat <code>.js</code> files governed by this <code>package.json</code> as ES modules. A nested <code>package.json</code> establishes its own scope. <code>.mjs</code> and <code>.cjs</code> files retain their respective module types.</p> <p>Set the <code>"type"</code> field to <code>"commonjs"</code> to make Node treat <code>.js</code> files in that scope as CommonJS.</p> <p>Some notes:</p> <ul> <li><p>The ES module system is the official standard for JavaScript.</p> </li> <li><p>Kevin Dangoor started the project that became CommonJS in January 2009, before JavaScript had an official standard module system.</p> </li> <li><p>ES modules became part of the ECMAScript standard in 2015. CommonJS remains supported in Node.js.</p> </li> </ul> <p>In this guide, we'll mainly use Node.js with ES modules, since ES modules are JavaScript's standard module system. To follow the examples, make sure the <code>"type"</code> field in your <code>package.json</code> file is set to <code>"module"</code>. You can do this by running the following command from your project directory:</p> <pre><code class="language-console">npm pkg set type=module </code></pre> <p>When relevant, we'll compare CommonJS syntax.</p> <p>Let's now discuss using Node.js to create web servers that receive and respond to requests from browsers.</p> <h2 id="heading-create-http-servers-with-nodejs">Create HTTP Servers with Node.js</h2> <p>An HTTP server lets your JavaScript application handle requests from clients, such as browsers, and send responses.</p> <p>There are three main steps to configuring an app's HTTP server:</p> <ol> <li><p>Create a new instance of the HTTP <code>Server</code> object.</p> </li> <li><p>Specify the system's port where you want the server to run.</p> </li> <li><p>Define how the server should respond to client requests.</p> </li> </ol> <p>Let's discuss the three steps in detail. To start, create an ES module for your project.</p> <pre><code class="language-console">touch server.js </code></pre> <p>Afterward, open the newly created module and initialize a new instance of the HTTP <code>Server</code> object.</p> <h3 id="heading-initialize-a-new-instance-of-the-http-server-object">Initialize a New Instance of the HTTP <code>Server</code> Object</h3> <p>Node provides the <code>createServer</code> method for creating an instance of the HTTP <code>Server</code> object (<code>http.Server</code>).</p> <p>You can use it in your project by importing it from Node's <code>http</code> module as follows:</p> <p><code>server.js</code></p> <pre><code class="language-javascript">import { createServer } from "node:http"; const server = createServer(); </code></pre> <p>Here's what's going on:</p> <ul> <li><p><code>import</code> statement: Imports the <code>createServer</code> API from Node's <code>http</code> library to the <code>server.js</code> ES module.</p> </li> <li><p><code>server</code>: A variable for storing the HTTP <code>Server</code> object that the <code>createServer()</code> function outputs.</p> </li> </ul> <p>Here's the CommonJS alternative:</p> <p><code>server.cjs</code></p> <pre><code class="language-javascript">const http = require("node:http"); const server = http.createServer(); </code></pre> <p>Once you have created the local HTTP <code>Server</code> object, specify the server's port.</p> <h3 id="heading-configure-the-systems-port-where-the-server-should-run">Configure the System's Port Where the Server Should Run</h3> <p>The HTTP <code>Server</code> object provides a <code>listen()</code> method to configure the port on which the server should run and accept client requests.</p> <h4 id="heading-syntax">Syntax</h4> <pre><code class="language-javascript">import { createServer } from "node:http"; const server = createServer(); server.listen(port, hostname, backlog, callback); </code></pre> <p>This form of the <code>listen()</code> method accepts the following arguments:</p> <ul> <li><p><code>port</code>: (number) The port number where the server should run and listen for client requests. If omitted, the operating system will assign any unused port. You can use <code>server.address().port</code> to retrieve the port the server is listening on after it starts listening for client connections.</p> </li> <li><p><code>hostname</code>: (string) The hostname or IP address on which the server should accept client connections. If omitted, the server will default to either an unspecified <a href="https://en.wikipedia.org/wiki/IPv6_address#Unspecified_address">IPv6 (<code>::</code>)</a> address or an <a href="https://en.wikipedia.org/wiki/0.0.0.0">IPv4 (<code>0.0.0.0</code>)</a> address. These unspecified addresses bind to all network interfaces for the applicable address family. You can use <code>server.address().address</code> to retrieve the bound IP address once it starts listening for client connections.</p> </li> <li><p><code>backlog</code>: (number) Maximum length of the pending connections' queue. 511 is the default value.</p> </li> <li><p><code>callback</code>: (function) The function to execute once the server starts listening for client requests.</p> </li> </ul> <h4 id="heading-example">Example</h4> <p><code>server.js</code></p> <pre><code class="language-javascript">import { createServer } from "node:http"; const server = createServer(); server.listen(3000, "127.0.0.1", 511, () =&gt; { const info = server.address(); console.log(`Server running at http://${info.address}: ${info.port}/`); }); </code></pre> <p>Here's what's going on:</p> <ul> <li><p><code>import</code> statement: Imports the <code>createServer</code> API from Node's <code>http</code> library to the <code>server.js</code> ES module.</p> </li> <li><p><code>server</code>: A variable for storing the HTTP <code>Server</code> object that the <code>createServer()</code> function outputs.</p> </li> <li><p><code>server.listen()</code>: Starts the server to listen for client connections. (Tip: The method emits a <a href="https://nodejs.org/api/net.html#event-listening"><code>listening</code></a> event once the server starts successfully.)</p> </li> </ul> <p>Here's the CommonJS alternative:</p> <p><code>server.cjs</code></p> <pre><code class="language-javascript">const http = require("node:http"); const server = http.createServer(); server.listen(3000, "127.0.0.1", 511, () =&gt; { const info = server.address(); console.log(`Server running at http://${info.address}: ${info.port}/`); }); </code></pre> <h4 id="heading-what-is-the-127001-address">What is the <code>127.0.0.1</code> address?</h4> <p>The <code>127.0.0.1</code> address is an IPv4 loopback address that refers to your local computer. The hostname <code>localhost</code> also refers to your local computer, but it may resolve to <code>127.0.0.1</code> or the IPv6 loopback address, <code>::1</code>. You can use it as follows:</p> <p><code>server.js</code></p> <pre><code class="language-javascript">import { createServer } from "node:http"; const server = createServer(); server.listen(3000, "localhost", 511, () =&gt; { console.log("Server running at http://localhost:3000/"); }); </code></pre> <p>If you run this server and visit its web address, the browser will wait for a response because we have not yet set up a request handler. Let's configure that now.</p> <h3 id="heading-configure-the-server-to-respond-to-client-requests">Configure the Server to Respond to Client Requests</h3> <p>The <code>createServer()</code> method accepts a <code>requestListener</code> callback that is invoked automatically whenever the server receives an HTTP request. Node allows you to use this callback to respond to requests.</p> <h4 id="heading-syntax">Syntax</h4> <p>The <code>createServer()</code> method accepts two optional arguments. Here's the syntax:</p> <pre><code class="language-javascript">import { createServer } from "node:http"; const server = createServer(options, callback); </code></pre> <ul> <li><p><code>options</code>: An object for customizing the server's behavior.</p> </li> <li><p><code>callback</code>: The <code>requestListener</code> function for handling and responding to client requests. It accepts two parameters:</p> </li> </ul> <pre><code class="language-javascript">import { createServer } from "node:http"; const server = createServer(options, function (request, response) { // the requestListener function's body }); </code></pre> <ul> <li><p><code>request</code> parameter: An <code>http.IncomingMessage</code> object that provides details about the client request.</p> </li> <li><p><code>response</code> parameter: An <code>http.ServerResponse</code> object for responding to the client requests.</p> </li> </ul> <p>Providing the <code>requestListener</code> callback function as <code>createServer</code>'s second argument causes Node.js to automatically register it as a listener for the <a href="https://nodejs.org/api/http.html#event-request"><code>"request"</code></a> event. So, the syntax above is equivalent to:</p> <pre><code class="language-javascript">import { createServer } from "node:http"; const server = createServer(options); server.on("request", function (request, response) { // the requestListener function's body }); </code></pre> <p><code>server.on("request", callback)</code> tells the server to listen for a request event and execute the callback on such an event.</p> <h4 id="heading-example">Example</h4> <p><code>server.js</code></p> <pre><code class="language-javascript">// Add the Node.js HTTP module import { createServer } from "node:http"; // Specify the hostname and port to run the server const hostname = "localhost"; const port = 3000; // Create a new Server instance with a requestListener callback const server = createServer((req, res) =&gt; { res.statusCode = 200; // Set an OK success (200) response status code res.setHeader("Content-Type", "text/plain"); // Define the media type of the response data res.end("Hello World!"); // Specify the response data and close the response stream }); // Run the server on the specified port and hostname server.listen(port, hostname, () =&gt; { console.log(`Server running at http://${hostname}: ${port}/`); }); </code></pre> <p>The snippet above used the <code>requestListener</code> callback function's response parameter to configure the server to respond to client requests. Here's the <code>server.on("request", callback)</code> alternative:</p> <p><code>server.js</code></p> <pre><code class="language-javascript">// Add the Node.js HTTP module import { createServer } from "node:http"; // Specify the hostname and port to run the server const hostname = "localhost"; const port = 3000; // Create a new Server instance const server = createServer(); // Listen for a request event and execute the requestListener callback server.on("request", (req, res) =&gt; { res.statusCode = 200; // Set an OK success (200) response status code res.setHeader("Content-Type", "text/plain"); // Define the media type of the response data res.end("Hello World!"); // Specify the response data and close the response stream }); // Run the server on the specified port and hostname server.listen(port, hostname, () =&gt; { console.log(`Server running at http://${hostname}: ${port}/`); }); </code></pre> <p>Now that your server is set up and the port configured, you can run the application.</p> <pre><code class="language-console">node server.js </code></pre> <ul> <li><p><code>node</code>: The command for running Node.js scripts or the REPL.</p> </li> <li><p><code>server.js</code>: The JavaScript file you want Node to run.</p> </li> </ul> <p>While the server is running, if users request the app's resource at the server's web address, they will see a <code>Hello World!</code> response as illustrated in the following image.</p> <img src="https://cdn.hashnode.com/uploads/covers/61672d1653401f641ba159b4/29085aef-058e-44f3-8e9a-fad24dc387b5.jpg" alt="The browser displays the &quot;Hello World!&quot; text at http://localhost:3000/" style="display: block;" width="600" height="400" loading="lazy"> <p><strong>Tip:</strong> Stop the running process using <code>Ctrl + C</code> on Windows, macOS, or Linux.</p> <p>Although the <code>node:http</code> module is Node.js's native API for handling HTTP requests, developers typically use frameworks to simplify the process. Some popular Node.js web frameworks include Express.js, Fastify, and Koa.js. Let's use Express.js as an example to see how a framework can simplify HTTP request handling in Node.</p> <h2 id="heading-what-exactly-is-expressjs">What Exactly Is Express.js?</h2> <p>Express.js is a web framework that simplifies server-side HTTP request handling in Node.js. It extends Node's HTTP module with built-in features such as routing and middleware, helping developers to build web applications and APIs efficiently without repetitive code. Here's how to use it in your Node.js project.</p> <h3 id="heading-1-install-express">1. Install Express</h3> <pre><code class="language-console">npm install express@5.2.1 </code></pre> <h3 id="heading-2-use-expressjs-to-configure-the-projects-nodejs-web-server">2. Use Express.js to Configure the Project's Node.js Web Server</h3> <p>Open the JavaScript server file and use Express.js to set up a web server:</p> <p><code>server.js</code></p> <pre><code class="language-javascript">// Add the Express module import express from "express"; // Create a new Express application instance const app = express(); // Set the port number where the server will run and listen for requests const port = 3000; // Create a route handler for GET requests to the "/" path app.get("/", (req, res) =&gt; res.send("Hello, world!")); // Run the server on the specified port app.listen(port, () =&gt; { console.log(`Server running at http://localhost:${port}`); }); </code></pre> <p>Here's what the code above does:</p> <ul> <li><p>Creates an Express application instance to access Express APIs, such as routers and middleware, simplifying HTTP request handling in Node.js.</p> </li> <li><p>Uses Express's <code>app.get()</code> method to define how the server handles HTTP GET requests to the <code>/</code> route.</p> </li> <li><p>Uses Express's <code>res.send()</code> method to send an HTTP response and automatically set the appropriate headers based on the data type provided.</p> </li> <li><p>Uses Express's <code>app.listen()</code> method to start the Node.js HTTP server on a specific port and execute a callback when the server begins listening for client requests.</p> </li> </ul> <p><strong>Tip:</strong></p> <ul> <li><p>In the statement <code>app.get("/", (req, res) =&gt; res.send("Hello, world!"))</code>,</p> <ul> <li><p><code>app.get()</code> is the Express routing method.</p> </li> <li><p><code>"/"</code> is the route path.</p> </li> <li><p><code>(req, res) =&gt; ...</code> is the route handler function.</p> </li> <li><p>The entire statement is the route (or route definition).</p> </li> </ul> </li> <li><p>The objects passed to an Express route handler's parameters (<code>req</code> and <code>res</code>) are enhanced versions of Node's <code>http.IncomingMessage</code> and <code>http.ServerResponse</code> objects. Express adds properties and helper methods to simplify handling HTTP requests and responses.</p> </li> <li><p>The order of route definitions matters. Express processes matching routes in the order they are defined. A handler can end the response or pass control to another handler using <code>next()</code>.</p> </li> </ul> <h3 id="heading-3-run-the-expressjs-web-server">3. Run the Express.js Web Server</h3> <p>Use the <code>node</code> command followed by the script's filename to run a Node.js script, including one that uses Express.js to handle HTTP requests.</p> <pre><code class="language-console">node server.js </code></pre> <p>Open <code>http://localhost:3000</code> in your browser to see <code>Hello, world!</code>.</p> <p><strong>Tip:</strong> Stop the server using <code>Ctrl + C</code> on Windows or <code>Control + C</code> on macOS.</p> <h2 id="heading-raw-nodejs-code-vs-expressjs-code">Raw Node.js Code vs. Express.js Code</h2> <p>While you can use Node's native HTTP API to handle web requests, it requires manual configuration, such as inspecting each request's method and URL, setting response headers, and converting objects to JSON.</p> <p>Express abstracts repetitive request-handling logic into declarative methods, simplifying Node.js web server development.</p> <p>Below are two examples comparing Node.js web server code written with the native HTTP module and with Express methods.</p> <h3 id="heading-example-1-use-raw-nodejs-to-handle-web-requests">Example 1: Use raw Node.js to Handle Web Requests</h3> <p>The following web server uses Node's built-in HTTP module to handle GET requests to two endpoints—home (<code>/</code>) and books (<code>/books</code>)—and return a 404 response for unmatched requests.</p> <p><code>server.js</code></p> <pre><code class="language-javascript">import { createServer } from "node:http"; const port = 3000; const server = createServer((req, res) =&gt; { if (req.method === "GET" &amp;&amp; req.url === "/") { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Welcome to the homepage!"); } else if (req.method === "GET" &amp;&amp; req.url === "/books") { const books = [ { id: 1, name: "Code React Sweetly" }, { id: 2, name: "Creating NPM Package" }, ]; res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify(books)); } else { res.writeHead(404, { "Content-Type": "text/plain" }); res.end("Page not found"); } }); server.listen(port, () =&gt; { console.log(`Raw Node server running at http://localhost:${port}`); }); </code></pre> <p>The snippet above uses Node's native <code>http</code> module to build a web server that accepts and responds to browser connections. In this raw Node.js example, you manually handle the following tasks:</p> <ul> <li><p><strong>Routing to the correct endpoint:</strong> <code>if (req.method === "..." &amp;&amp; req.url === "...")</code></p> </li> <li><p><strong>Response header configuration:</strong> <code>res.writeHead(...)</code></p> </li> <li><p><strong>JSON formatting:</strong> <code>JSON.stringify(...)</code></p> </li> </ul> <p>Now, let's see how to build a similar web server using Express. Run these examples one at a time, replacing the contents of <code>server.js</code> and restarting the server.</p> <h3 id="heading-example-2-use-expressjs-to-handle-web-requests">Example 2: Use Express.js to Handle Web Requests</h3> <p>The following web server uses Express routes to handle GET requests to two endpoints—home (<code>/</code>) and books (<code>/books</code>)—and return a 404 response for unmatched requests.</p> <p><code>server.js</code></p> <pre><code class="language-javascript">import express from "express"; const app = express(); const port = 3000; app.get("/", (req, res) =&gt; { res.send("Welcome to the homepage!"); }); app.get("/books", (req, res) =&gt; { const books = [ { id: 1, name: "Code React Sweetly" }, { id: 2, name: "Creating NPM Package" }, ]; res.json(books); }); app.use((req, res) =&gt; { res.status(404).send("Page not found"); }); app.listen(port, () =&gt; { console.log(`Express server running at http://localhost:${port}`); }); </code></pre> <p>The snippet above uses Express.js to build a web server that accepts and responds to browser connections. Express simplifies the following tasks:</p> <ul> <li><p><strong>Routing logic:</strong> Express.js provides simple route declarations like <code>app.get()</code>, <code>app.post()</code>, and <code>app.delete()</code>, eliminating the need for complex conditional ogic to check <code>req.url</code> and <code>req.method</code> for each route.</p> </li> <li><p><strong>Response header configuration:</strong> Express's <code>res.send()</code> method automatically sets appropriate response headers based on the response body. Use <code>res.status()</code> when you need to set a status code, as in the 404 handler above.</p> </li> <li><p><strong>JSON formatting:</strong> Express's <code>res.json()</code> method automatically converts JavaScript objects into valid JSON responses, handling serialization and headers for you.</p> </li> </ul> <h2 id="heading-important-stuff-to-know-about-creating-expressjs-web-servers-in-nodejs">Important Stuff to Know About Creating Express.js Web Servers in Node.js</h2> <p>Keep the following key points in mind when creating Express.js servers in your Node.js project.</p> <h3 id="heading-its-common-to-call-applisten-last">It's Common to Call <code>app.listen()</code> Last</h3> <p>It's common to call <code>app.listen()</code> last in an Express.js app. This way, all your routes, middleware, and error handlers are set up before the server starts listening for requests.</p> <h3 id="heading-you-can-set-the-port-number-using-environment-variables">You Can Set the Port Number Using Environment Variables</h3> <p>To read the port number from an environment variable, replace the existing <code>port</code> declaration with the following:</p> <pre><code class="language-javascript">const port = process.env.PORT || 3000; </code></pre> <p>The above snippet instructs Node to use the <code>PORT</code> environment variable's value, or default to <code>3000</code> if it's unset or empty. In a shell such as Bash, the following command sets <code>PORT</code> to <code>8000</code> for this run of the server:</p> <pre><code class="language-console">PORT=8000 node server.js </code></pre> <h3 id="heading-common-expressjs-response-methods">Common Express.js Response Methods</h3> <p>Here are some common response methods you'll use in Express.js:</p> <ul> <li><p><code>res.send()</code>: Send an HTTP response to the client and automatically set the appropriate response headers based on the data type provided as the method's argument.</p> </li> <li><p><code>res.json()</code>: Send JSON responses to the client and automatically set the response's <code>Content-Type</code> header to <code>application/json</code>.</p> </li> <li><p><code>res.redirect()</code>: Redirect the client's request to a different URL.</p> </li> <li><p><code>res.render()</code>: Render a template engine's view and send the resulting HTML string to the client.</p> </li> <li><p><code>res.status()</code>: Set the response's HTTP status code without ending the request-response cycle. You can chain other response methods to it, for example, <code>res.status(404).send("404 Error: Page not found")</code>. You do not need to call this method to use the default status code of <code>200</code>.</p> </li> <li><p><code>res.end()</code>: End the response. When called without a data argument, it sends no additional response body data. It can also accept data to send before ending the response.</p> </li> </ul> <h3 id="heading-expresss-response-methods-do-not-terminate-the-http-request-handlers-execution">Express's Response Methods Do Not Terminate the HTTP Request Handler's Execution</h3> <p>While response methods like <code>res.send()</code> close the HTTP request-response cycle, they do not end the execution of the route handler function.</p> <p><strong>Here's an example:</strong></p> <pre><code class="language-javascript">app.get("/", (req, res) =&gt; { // This sends a text response to the client and ends the request-response cycle, but does not end the route handler's execution res.send("Hello, client!"); // This logs the string to the console because the function is still running console.log("Hello, devs!"); // This will cause an error because the request-response cycle is closed, so you cannot send additional responses to the client during the callback's execution res.send("Hello, client again!"); }); </code></pre> <p>The <code>console.log()</code> statement in the example above works because the request handler is still running. The <code>res.send("Hello, client!")</code> line ends the response to that request. It does not stop the callback's execution.</p> <p>Note that the <code>app.get(...)</code> code in the snippet above is called an Express route. But what exactly is a route in Express.js? Let's discuss it now.</p> <h2 id="heading-what-is-a-route-in-expressjs">What Is a Route in Express.js?</h2> <p>A route in Express.js consists of an HTTP method, a URL path, and a chain of one or more middleware functions for processing client requests to an endpoint.</p> <p><strong>Tip:</strong></p> <ul> <li><p>An endpoint consists of an HTTP method and a URL path. (Endpoint = HTTP method + URL path)</p> </li> <li><p>The route includes an HTTP method, a URL path, and the server-side logic that handles client requests. (Route = Server HTTP method + Server URL path + handler function)</p> </li> </ul> <h3 id="heading-syntax-of-a-route-in-express">Syntax of a Route in Express</h3> <pre><code class="language-javascript">app.METHOD(PATH, HANDLER); </code></pre> <ul> <li><p><code>app</code>: An Express application instance that provides access to Express APIs, including methods and middleware, to simplify HTTP request handling in Node.js.</p> </li> <li><p><code>METHOD</code>: A lowercase HTTP request method, such as <code>get</code>, <code>post</code>, or <code>delete</code>.</p> </li> <li><p><code>PATH</code>: The URL path the route will handle. It can be a string or a regular expression. Express 4 also supports string patterns whose syntax differs in Express 5.</p> </li> <li><p><code>HANDLER</code>: The callback (or middleware) function that Express executes when the request and route endpoints correspond. The handler may be:</p> <ul> <li><p>A single function</p> </li> <li><p>More than one function</p> </li> <li><p>An array of functions</p> </li> <li><p>A combination of an array of functions and individual function arguments.</p> </li> </ul> </li> </ul> <p><strong>Tip:</strong></p> <ul> <li><p>You can use the <code>app.all()</code> method to set up handler functions that run for every supported HTTP request method on a specific <code>PATH</code>.</p> </li> <li><p>The <code>app.use()</code> method lets you set up middleware functions that can run for all HTTP request methods and paths when a request reaches them. If you specify a path argument, <code>app.use()</code> matches that path and its subpaths. For example, <code>/book</code> matches <code>/book</code>, <code>/book/dashboard</code>, and <code>/book/dashboard/author/202605</code>.</p> </li> </ul> <h3 id="heading-middleware-vs-route-handler">Middleware vs. Route Handler</h3> <p>Developers often use the term "route handler" to describe the final function responsible for resolving the request and sending the response, whereas preceding reusable functions are referred to as "middleware".</p> <pre><code class="language-javascript">app.METHOD(PATH, MIDDLEWARE1, MIDDLEWARE2, HANDLER); </code></pre> <p><strong>Note:</strong> Express does not mandate distinct names or categories for functions attached to a route. From Express's perspective, they are all handler functions, with <code>next()</code> passing control from one to the next. However, developers often differentiate between middleware and route handlers to enhance code organization, readability, and communication.</p> <ul> <li><p>Middleware functions, often stored in a <code>/middlewares</code> directory, serve as utility components that perform cross-cutting or preparatory tasks such as authentication, validation, logging, and data loading. These functions are often reusable and typically invoke <code>next()</code> to pass control to the next function.</p> </li> <li><p>Route handlers, sometimes organized as controllers in a <code>/controllers</code> directory, contain the business logic that generates the endpoint's primary response, such as <code>getUserProfile</code>, <code>createBlogPost</code>, or <code>deleteAccount</code>. These functions may be specific to a route and usually conclude the request-response cycle by invoking methods such as <code>res.send()</code> or <code>res.json()</code>.</p> </li> </ul> <h3 id="heading-route-examples-in-expressjs">Route Examples in Express.js</h3> <p>Here are some examples of routes in Express.js.</p> <h4 id="heading-express-route-with-a-single-handler-function">Express route with a single handler function</h4> <pre><code class="language-javascript">app.get("/single/handler", (req, res) =&gt; { res.send("Request handled with a single handler function!"); }); </code></pre> <p>The example above uses a single callback function to handle GET requests to the <code>GET /single/handler</code> endpoint. The handler function, in this case, is an inline controller. A common approach is to keep controllers in a <code>controllers</code> directory. This makes the code easier to test and maintain while keeping the route definition readable.</p> <p><strong>Here's an example:</strong></p> <p>The following example shows a <code>GET /single/handler</code> route with a modular controller that has been moved to its own module (<code>controllers/single-handler.js</code>). This approach separates route definitions from the request-handling logic that generates the endpoint's response.</p> <pre><code class="language-javascript">import express from "express"; import * as controller from "./controllers/single-handler.js"; const app = express(); const port = 3000; app.get("/single/handler", controller.singleHandler); app.listen(port, () =&gt; { console.log(`Express server running at http://localhost:${port}`); }); </code></pre> <p>Below is an example of a controller file that contains only the <code>singleHandler</code> handler function.</p> <p><code>controllers/single-handler.js</code></p> <pre><code class="language-javascript">function singleHandler(req, res) { res.send("Request handled with a single handler function!"); } export { singleHandler }; </code></pre> <p><strong>Tip:</strong> A controller is a route handler or group of handlers that manages requests for specific endpoints. Controllers are often placed in separate modules to improve code organization and maintain a clear separation of concerns.</p> <h4 id="heading-express-route-with-multiple-middleware-functions">Express route with multiple middleware functions</h4> <pre><code class="language-javascript">app.get( "/multiple/middleware", (req, res, next) =&gt; { console.log("First callback: passing control to the next middleware &gt;"); next(); }, (req, res, next) =&gt; { console.log("Second callback: passing control to the next middleware &gt;"); next(); }, (req, res) =&gt; { res.send("Request handled with multiple middleware functions!"); }, ); </code></pre> <p>The example above uses multiple middleware functions to handle GET requests to the <code>/multiple/middleware</code> path.</p> <h4 id="heading-express-route-with-an-array-of-middleware-functions">Express route with an array of middleware functions</h4> <pre><code class="language-javascript">const arrayOfCallbacks = [ function callback1(req, res, next) { console.log("First callback: passing control to the next middleware &gt;"); next(); }, function callback2(req, res, next) { console.log("Second callback: passing control to the next middleware &gt;"); next(); }, function callback3(req, res) { res.send("Request handled with an array of middleware functions!"); }, ]; app.get("/array/middleware", arrayOfCallbacks); </code></pre> <p>The example above uses an array of callback functions to handle GET requests to the <code>GET /array/middleware</code> endpoint.</p> <h4 id="heading-express-route-with-a-combination-of-an-array-of-middleware-functions-and-individual-callbacks">Express route with a combination of an array of middleware functions and individual callbacks</h4> <pre><code class="language-javascript">const arrayOfCallbacks = [ function callback1(req, res, next) { console.log("First callback: passing control to the next middleware &gt;"); next(); }, function callback2(req, res, next) { console.log("Second callback: passing control to the next middleware &gt;"); next(); }, ]; app.get( "/mix/middleware", arrayOfCallbacks, (req, res, next) =&gt; { console.log("Third callback: passing control to the next middleware &gt;"); next(); }, (req, res, next) =&gt; { console.log("Fourth callback: passing control to the next middleware &gt;"); next(); }, (req, res) =&gt; { res.send( "Request handled with a combination of an array of middleware functions and independent callback arguments!", ); }, ); </code></pre> <p>The example above uses both an array of callbacks and individual function arguments to handle GET requests to the <code>/mix/middleware</code> path.</p> <h3 id="heading-important-things-to-know-about-the-next-parameter">Important Things to Know About the <code>next</code> Parameter</h3> <ul> <li><p><code>next()</code> passes control to the next middleware function in the stack.</p> </li> <li><p><code>next("route")</code> skips the remaining handlers for the current route and continues looking for a matching route. Matching considers both the request method and path. It is only effective within middleware functions defined for <code>app.METHOD()</code> or <code>router.METHOD()</code>.</p> </li> <li><p><code>next(new Error(value))</code> passes control to the next error-handling middleware.</p> </li> <li><p>If the currently running middleware does not end the request-response cycle, include a <code>next</code> parameter and invoke it within the callback to pass control to the next middleware. Otherwise, the client's request will remain pending.</p> </li> </ul> <h3 id="heading-categories-of-middleware-in-expressjs">Categories of Middleware in Express.js</h3> <p>Express middleware can be classified into five primary categories:</p> <ul> <li><p>Application-level</p> </li> <li><p>Router-level</p> </li> <li><p>Error-handling</p> </li> <li><p>Built-in</p> </li> <li><p>Third-party</p> </li> </ul> <p>These categories describe where middleware is mounted, how it behaves, or its source.</p> <h4 id="heading-application-level-middleware">Application-level middleware</h4> <p>This type of middleware is attached to the Express application instance (<code>app</code>).</p> <p><strong>Example:</strong></p> <pre><code class="language-javascript">import express from "express"; const app = express(); app.get("/profile", (req, res, next) =&gt; { console.log("Hi, there!"); next(); }); </code></pre> <h4 id="heading-router-level-middleware">Router-level middleware</h4> <p>This middleware is attached to an Express Router instance (<code>router</code>). It operates similarly to application-level middleware but is mounted on a router instead of the application.</p> <p><strong>Example:</strong></p> <pre><code class="language-javascript">import express from "express"; const router = express.Router(); router.get("/profile", (req, res, next) =&gt; { console.log("Hi, there!"); next(); }); </code></pre> <h4 id="heading-error-handling-middleware">Error-handling middleware</h4> <p>This middleware handles errors during request processing and is identified by its four-parameter signature.</p> <pre><code class="language-javascript">import express from "express"; const app = express(); app.use((err, req, res, next) =&gt; { console.error(err); res.status(500).send("Error 500: Encountered an unexpected error"); }); </code></pre> <p><strong>Note:</strong></p> <ul> <li><p>All four parameters must be present, even if some are unused. Otherwise, Express won't recognize the function as error-handling middleware. Express uses the number of declared parameters, not their names, to identify such middleware.</p> </li> <li><p>Define error-handling middleware after the routes and middleware whose errors it should handle. This placement ensures it processes errors passed from preceding middleware functions.</p> </li> </ul> <h4 id="heading-built-in-middleware">Built-in middleware</h4> <p>These middleware functions are included with Express and available by default, without requiring additional package installations.</p> <p><strong>Examples:</strong></p> <pre><code class="language-javascript">import express from "express"; express.static("public"); express.json(); express.urlencoded(); </code></pre> <h4 id="heading-third-party-middleware">Third-party middleware</h4> <p>These middleware functions are provided by external packages developed and shared by the broader Express community. Examples include cors, morgan, and helmet.</p> <pre><code class="language-typescript">import express from "express"; import cors from "cors"; const app = express(); app.use(cors()); </code></pre> <h4 id="heading-note-middleware-categories-in-express-are-not-mutually-exclusive">Note: Middleware categories in Express are not mutually exclusive</h4> <p><strong>Example 1:</strong></p> <pre><code class="language-javascript">import express from "express"; const app = express(); app.use(express.json()); </code></pre> <p>The <code>express.json()</code> middleware in the snippet above is both:</p> <ul> <li><p>Application-level, because it is attached to the <code>app</code> instance</p> </li> <li><p>Built-in, because Express provides it</p> </li> </ul> <p><strong>Example 2:</strong></p> <pre><code class="language-javascript">import express from "express"; import cors from "cors"; const router = express.Router(); router.use(cors()); </code></pre> <p>The <code>cors()</code> middleware in the snippet above is both:</p> <ul> <li><p>Router-level, because it is attached to the <code>router</code> instance</p> </li> <li><p>Third-party, because a package outside of Express provided it</p> </li> </ul> <p>Are you asking what the <code>express.Router()</code> method in the example above is? Let's discuss it.</p> <h2 id="heading-what-is-the-expressrouter-method">What Is the <code>express.Router()</code> Method?</h2> <p>The <code>express.Router()</code> method creates a router object that groups related routes and middleware. You can attach this router to an Express application or another router using <code>app.use()</code> or <code>router.use()</code>.</p> <img src="https://cdn.hashnode.com/uploads/covers/61672d1653401f641ba159b4/1ae7a155-57df-42dd-ae99-940c92f3f1f3.jpg" alt="Why is Express Router a mini-app?" style="display: block;" width="600" height="400" loading="lazy"> <p>As illustrated in the image above, a router is often called a mini-app because it provides a complete middleware and routing system. It helps organize applications by allowing you to define related routes in one place and mount them onto the main Express application. For instance, suppose your Express application needs to handle the following categories of routes and middleware:</p> <ul> <li><p><strong>Index:</strong> For index-related routes and middleware.</p> </li> <li><p><strong>Books:</strong> For book-related routes and middleware.</p> </li> <li><p><strong>Videos:</strong> For video-related routes and middleware.</p> </li> </ul> <p>In this situation, you can use the <code>express.Router()</code> method to group related routes and middleware. This keeps things organized and makes each group easier to manage and reuse.</p> <p>While you can create all router and app instances in one file, developers usually place routers in separate files for better organization and maintainability. Let's follow this approach by creating separate files for each route and middleware category.</p> <h3 id="heading-create-a-directory-for-the-routers">Create a Directory for the Routers</h3> <p>Create a <code>routes</code> directory at your project's root to store all the app's routers.</p> <pre><code class="language-console">mkdir routes </code></pre> <p><strong>Note:</strong> You can name this folder whatever you like, but <code>routes</code> is a common name since it holds the files that define your app's routes (URL endpoints). The <code>express.Router()</code> method is just a tool that Express provides for managing those routes.</p> <h3 id="heading-create-the-route-files">Create the Route Files</h3> <p>A route file is a module where you define related routes and export an Express router object. Each file usually represents a resource or feature, such as users, books, or authentication.</p> <p>Create the route files in the <code>routes</code> directory.</p> <pre><code class="language-console">touch routes/index.js routes/books.js routes/videos.js </code></pre> <h3 id="heading-create-a-mini-app-for-the-book-related-routes-and-middleware">Create a Mini-App for the Book-Related Routes and Middleware</h3> <p>Open the <code>books.js</code> file and use an <code>express.Router()</code> instance to group all the book-related routes and middleware.</p> <p><code>routes/books.js</code></p> <pre><code class="language-javascript">// Import the Router function from Express import { Router } from "express"; // Create a new Express router instance const bookRouter = Router(); // Define middleware specific to this router instance bookRouter.use((req, res, next) =&gt; { console.log("Book route accessed at: ", Date.now()); next(); }); // Define routes specific to this router instance: bookRouter.get("/", (req, res) =&gt; { res.send("&lt;h1&gt;List of All Books&lt;/h1&gt;"); }); bookRouter.get("/:id", (req, res) =&gt; { res.type("text/plain").send(`Details for book ${req.params.id}`); }); bookRouter.get("/:id/draft", (req, res) =&gt; { // Escape the ID before inserting it into HTML text const bookId = req.params.id .replaceAll("&amp;", "&amp;amp;") .replaceAll("&lt;", "&amp;lt;") .replaceAll("&gt;", "&amp;gt;"); res.send(`&lt;h1&gt;New Draft for Book ${bookId}&lt;/h1&gt;`); }); export { bookRouter }; </code></pre> <p>Here's what the code above does:</p> <ul> <li><p>Creates a <code>bookRouter</code> instance to access Express APIs, such as <code>.get()</code>, <code>.post()</code>, <code>.use()</code>, and <code>.route()</code>.</p> </li> <li><p>Uses Express's <code>router.use()</code> method to mount middleware onto the <code>bookRouter</code> instance.</p> </li> <li><p>Uses Express's <code>router.get()</code> method to define how the server handles HTTP GET requests.</p> </li> <li><p>Uses Express's <code>res.send()</code> method to send an HTTP response and automatically set the appropriate headers based on the data type provided. The <code>/:id</code> handler explicitly sets the response type to plain text, while the <code>/:id/draft</code> handler escapes the ID before inserting it into HTML so it is displayed as text.</p> </li> <li><p>Uses the <code>export</code> statement to make the router object available for import wherever it is needed in the application.</p> </li> </ul> <h3 id="heading-time-to-practice-with-routers">Time to Practice with Routers</h3> <p>This is your moment to try out what you've learned about Express routers.</p> <p>For this exercise, create mini-apps for the project's index and video-related routes and middleware. Export them as <code>indexRouter</code> from <code>routes/index.js</code> and <code>videoRouter</code> from <code>routes/videos.js</code> to match the imports in the next section.</p> <p>Take a moment to try this on your own before moving on. If you need help, review the <code>books.js</code> file. Practicing will help you understand better.</p> <p>After you finish the exercise, move on to setting up the main Express app.</p> <h3 id="heading-update-the-main-express-application">Update the Main Express Application</h3> <p>Open your project's <code>server.js</code> file and attach the routers to the main Express application.</p> <p><code>server.js</code></p> <pre><code class="language-javascript">// Add the Express module import express from "express"; // Import the routers from their separate files import { indexRouter } from "./routes/index.js"; import { bookRouter } from "./routes/books.js"; import { videoRouter } from "./routes/videos.js"; // Create a new Express application instance const app = express(); // Specify the host and port to receive requests const host = "localhost"; const port = 3000; // Mount the routers to specific paths: // Mount indexRouter at / app.use("/", indexRouter); // Mount bookRouter at /books and its subpaths app.use("/books", bookRouter); // Mount videoRouter at /videos and its subpaths app.use("/videos", videoRouter); // Run the server on the specified port and host app.listen(port, host, () =&gt; { console.log(`Server running live at http://${host}: ${port}!`); }); </code></pre> <p>Here's what the code above does:</p> <ul> <li><p>Creates an Express application instance to access Express APIs, such as routers and middleware, which simplifies HTTP request handling in Node.js.</p> </li> <li><p>Uses Express's <code>app.use()</code> method to add the mini-apps to the main Express application.</p> </li> <li><p>Uses Express's <code>app.listen()</code> method to start the Node.js HTTP server on a specific port and execute a callback when the server begins listening for client requests.</p> </li> </ul> <p><strong>Note:</strong> The <code>app.use()</code> method's path serves as the router's base path or mount path. Routes defined in the router are matched relative to this mount path. For example:</p> <ul> <li><p><code>bookRouter</code>'s <code>/</code> route matches requests to <code>/books</code>.</p> </li> <li><p><code>bookRouter</code>'s <code>/:id</code> route matches requests to <code>/books/:id</code>.</p> </li> <li><p><code>bookRouter</code>'s <code>/:id/draft</code> route matches requests to <code>/books/:id/draft</code>.</p> </li> </ul> <h3 id="heading-run-your-expressjs-application">Run your Express.js Application</h3> <p>Once you've set up the routers, run your app by starting the server file with Node.js.</p> <pre><code class="language-console">node --watch server.js </code></pre> <p>Then, check your app running live at the specified endpoints. For example, <code>http://localhost:3000/books</code>.</p> <p><strong>Tip:</strong> Stop the running process using <code>Ctrl + C</code> on Windows, macOS, or Linux.</p> <h3 id="heading-important-things-to-know-about-express-routers">Important Things to Know About Express Routers</h3> <p>Here are some important things to remember when using Express routers.</p> <h4 id="heading-routers-do-not-inherit-parent-route-parameters-by-default">Routers do not inherit parent route parameters by default</h4> <p>By default, Express doesn't pass parameters from a router's mount path to routes inside that router.</p> <p><strong>Example:</strong></p> <pre><code class="language-javascript">// Add the Express module import express from "express"; // Create a new Express application instance const app = express(); // Create a new Express router instance const bookRouter = express.Router(); // Define a sub-route bookRouter.get("/books/:bookId", (req, res) =&gt; { const topic = req.params.topic; // undefined by default const bookId = req.params.bookId; res.json({ topic, bookId }); }); // Define a parent route app.use("/codesweetly/:topic", bookRouter); // Run the server on the specified port and host const server = app.listen(3000, "127.0.0.1", () =&gt; { const { address, port } = server.address(); console.log(`Server running live at http://${address}: ${port}!`); }); </code></pre> <p>If you run the web server in the example above and enter the following URL in the browser:</p> <pre><code class="language-txt">http://127.0.0.1:3000/codesweetly/CSS/books/91827 </code></pre> <p>The <code>req.params.topic</code> parameter will be <code>undefined</code> because sub-routes can't access the parent route's path parameter by default. So, the server will only send a <code>{"bookId":"91827"}</code> response to the client.</p> <p>To let a child route access its parent's path parameter, set <code>mergeParams</code> to <code>true</code> when you create the router.</p> <p><strong>Example:</strong></p> <pre><code class="language-javascript">// Add the Express module import express from "express"; // Create a new Express application instance const app = express(); // Create a new Express router instance const bookRouter = express.Router({ mergeParams: true }); // Define a sub-route bookRouter.get("/books/:bookId", (req, res) =&gt; { const topic = req.params.topic; // "CSS" for the URL below const bookId = req.params.bookId; res.json({ topic, bookId }); }); // Define a parent route app.use("/codesweetly/:topic", bookRouter); // Run the server on the specified port and host const server = app.listen(3000, "127.0.0.1", () =&gt; { const { address, port } = server.address(); console.log(`Server running live at http://${address}: ${port}!`); }); </code></pre> <p>If you run the web server in the example above and enter the following URL in your browser:</p> <pre><code class="language-txt">http://127.0.0.1:3000/codesweetly/CSS/books/91827 </code></pre> <p>The <code>req.params.topic</code> parameter will be <code>CSS</code> because <code>mergeParams: true</code> lets the sub-route access the parent's path parameter. So, the server will send a <code>{"topic":"CSS","bookId":"91827"}</code> response to the client.</p> <p><strong>Tip:</strong></p> <ul> <li><p>The line <code>const { address, port } = server.address()</code> uses <a href="https://codesweetly.com/destructuring-object">object destructuring</a> to get the <code>address</code> and <code>port</code> values from the <code>server.address()</code> object.</p> </li> <li><p>The <code>app.listen()</code> method returns a native Node.js <code>http.Server</code> instance, allowing access to underlying server APIs such as <code>.address()</code>, <code>.on()</code>, and <code>.maxHeadersCount</code>.</p> </li> <li><p><code>127.0.0.1</code> is an IPv4 loopback address. <code>localhost</code> may resolve to <code>127.0.0.1</code> or the IPv6 loopback address <code>::1</code>. Use <code>http://127.0.0.1:3000</code> for these examples because the server explicitly listens on that IPv4 address.</p> </li> </ul> <h4 id="heading-routers-can-be-mounted-on-other-routers">Routers can be mounted on other routers</h4> <p>You can attach one Express router to another.</p> <p><strong>Example:</strong></p> <pre><code class="language-javascript">// Add the Express module import express from "express"; // Create a new Express application instance const app = express(); // Create two Express router instances const htmlRouter = express.Router(); const bookRouter = express.Router(); // Define a sub-route bookRouter.get("/:bookId", (req, res) =&gt; { res .type("text/plain") .send(`Details for the HTML book with ID ${req.params.bookId}`); }); // Mount bookRouter on htmlRouter's /books path htmlRouter.use("/books", bookRouter); // Mount htmlRouter at /html and its subpaths app.use("/html", htmlRouter); // Run the server on the specified port and host const server = app.listen(3000, "127.0.0.1", () =&gt; { const { address, port } = server.address(); console.log(`Server running live at http://${address}: ${port}!`); }); </code></pre> <p>The <code>bookRouter</code>'s <code>GET /:bookId</code> route matches requests to <code>GET /html/books/:bookId</code> because, in this example, the book router is attached to <code>htmlRouter</code>.</p> <p>Therefore, if you run the web server in the example above and enter the following URL in the browser:</p> <pre><code class="language-txt">http://127.0.0.1:3000/html/books/91827 </code></pre> <p>The server will send a "Details for the HTML book with ID 91827" response to the client.</p> <p>Now that you know what a router is, let's discuss how to use views to define the content and structure of the webpage your Express.js app returns to users.</p> <h2 id="heading-what-is-a-view-in-expressjs">What Is a View in Express.js?</h2> <p>A view is a template or document that defines the content and structure of a webpage returned to users by an Express.js application. It can be a static HTML file or a dynamic template rendered into HTML at <a href="https://codesweetly.com/web-tech-terms-r/#runtime">runtime</a> by a template engine.</p> <p>In this guide, we'll work with two types of views:</p> <ul> <li><p>Static views</p> </li> <li><p>Dynamic views</p> </li> </ul> <h3 id="heading-what-are-static-views-in-expressjs">What Are Static Views in Express.js?</h3> <p>Static views contain the final HTML that the server sends to the browser as-is. For example, let's configure your Express.js application to serve four static views.</p> <h4 id="heading-create-static-views">Create static views</h4> <p>Create <code>index.html</code>, <code>about.html</code>, <code>contact.html</code>, and <code>404.html</code> files in your project's root directory.</p> <pre><code class="language-console">touch index.html about.html contact.html 404.html </code></pre> <p>Open each view and add the following HTML content:</p> <h5 id="heading-indexhtml-homepage">index.html (homepage)</h5> <pre><code class="language-html">&lt;!doctype html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;meta charset="UTF-8" /&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt; &lt;title&gt;Static Views in Express.js | CodeSweetly Tutorial&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;Welcome to the Static View Guide&lt;/h1&gt; &lt;p&gt;Site's pages:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="/about"&gt;About&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="/contact"&gt;Contact&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <h5 id="heading-abouthtml-about-page">about.html (about page)</h5> <pre><code class="language-html">&lt;!doctype html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;meta charset="UTF-8" /&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt; &lt;title&gt;About Us | Static Views in Express.js&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;About Us&lt;/h1&gt; &lt;p&gt; Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero soluta voluptas reprehenderit minus veniam! Corrupti a esse quidem nostrum harum, explicabo tempore tempora aut, sint et voluptatem magni ea vel? &lt;/p&gt; &lt;div&gt;&lt;a href="/"&gt;Return to the home page&lt;/a&gt;&lt;/div&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <h5 id="heading-contacthtml-contact-page">contact.html (contact page)</h5> <pre><code class="language-html">&lt;!doctype html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;meta charset="UTF-8" /&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt; &lt;title&gt;Contact | Static Views in Express.js&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;Contact Us&lt;/h1&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="https://codesweetly.com"&gt;Website&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="https://x.com/oluwatobiss"&gt;X (Twitter)&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;div&gt;&lt;a href="/"&gt;Return to the home page&lt;/a&gt;&lt;/div&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <h5 id="heading-404html-error-page">404.html (error page)</h5> <pre><code class="language-html">&lt;!doctype html&gt; &lt;html lang="en"&gt; &lt;head&gt; &lt;meta charset="UTF-8" /&gt; &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt; &lt;title&gt;404 - Page not found | Static Views in Express.js&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;Page not found&lt;/h1&gt; &lt;div&gt;Sorry, we couldn't find that page&lt;/div&gt; &lt;div&gt;&lt;a href="/"&gt;Go back to the home page&lt;/a&gt;&lt;/div&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>Next, configure your project to handle browser connections.</p> <h4 id="heading-configure-the-projects-web-server-to-serve-static-views">Configure the project's web server to serve static views</h4> <p>Open the project's <code>server.js</code> module and configure the web server to serve the appropriate static HTML view in response to requests.</p> <pre><code class="language-javascript">// Add the required modules import express from "express"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; // Create a new Express application instance const app = express(); // Specify the hostname and port to receive requests const host = "localhost"; const port = 3000; // Get the directory name of the current file's path const __dirname = dirname(fileURLToPath(import.meta.url)); // Use the index.html static view as a response to 'GET /' requests app.get("/", (req, res) =&gt; res.sendFile(join(__dirname, "index.html"))); // Use the about.html static view as a response to 'GET /about' requests app.get("/about", (req, res) =&gt; { res.sendFile(join(__dirname, "about.html")); }); // Use the contact.html static view as a response to 'GET /contact' requests app.get("/contact", (req, res) =&gt; { res.sendFile(join(__dirname, "contact.html")); }); // Use the 404.html static view as a response to unmatched requests app.use((req, res) =&gt; { res.status(404).sendFile(join(__dirname, "404.html")); }); // Run the server on the specified port and hostname app.listen(port, host, () =&gt; { console.log(`Server running live at http://${host}: ${port}`); }); </code></pre> <p>Here are the main things the snippet above does:</p> <ul> <li><p>Create an Express application instance to access Express APIs, such as routers and middleware, which simplify HTTP request handling in Node.js.</p> </li> <li><p>Get the directory name of the current file's path.</p> <ul> <li><p><code>dirname()</code> returns a path's directory name.</p> </li> <li><p><code>fileURLToPath()</code> converts a URL to a valid path string.</p> </li> <li><p><code>import.meta.url</code> returns the absolute URL of the current file.</p> </li> </ul> </li> <li><p>Create routes to receive and respond to clients' requests.</p> <ul> <li><p><code>app</code> is an Express application instance.</p> </li> <li><p>The <code>app.get()</code> method defines how the server handles HTTP GET requests to the specified path (the method's first argument).</p> </li> <li><p>The <code>res.sendFile()</code> method sends a file from the server to the client.</p> </li> <li><p><code>join()</code> joins multiple path segments into a single, normalized path string using the current operating system’s path separator.</p> </li> <li><p>The <code>app.use()</code> method lets you set up handler functions that Express runs for all HTTP request methods and paths. If you specify a path argument, <code>app.use()</code> treats it as a prefix for matching routes. For example, <code>/book</code> matches <code>/book</code>, <code>/book/dashboard</code>, and <code>/book/dashboard/author/202605</code>.</p> </li> <li><p><code>res.status(404)</code> sets the HTTP status code to 404 for a request that reaches this final handler without receiving a response.</p> </li> </ul> </li> <li><p>Use Express's <code>app.listen()</code> method to start the Node.js HTTP server on a specific port and execute a callback when the server begins listening for client requests.</p> </li> </ul> <h4 id="heading-run-your-expressjs-application">Run your Express.js application</h4> <p>Once you've set up the server, run it with Node.js.</p> <pre><code class="language-console">node --watch server.js </code></pre> <p>Afterward, check your app running live at <code>http://localhost:3000</code>.</p> <p><strong>Tip:</strong> Stop the running process using <code>Ctrl + C</code> on Windows, macOS, or Linux.</p> <p>To keep things organized, put all your static views in a <code>public</code> directory. This is a common practice.</p> <h4 id="heading-create-a-directory-for-static-views">Create a directory for static views</h4> <p>Create a <code>public</code> directory at your project's root to store all static views.</p> <pre><code class="language-console">mkdir public </code></pre> <p><strong>Note:</strong> You can name this folder whatever you like, but <code>public</code> is a common choice to indicate that its files (HTML, CSS, client-side JavaScript, images) are intended to be served to clients. The folder's name alone does not make its contents accessible. The server must be configured to serve them. Don't put sensitive information in it.</p> <h4 id="heading-move-all-static-views-into-the-public-folder">Move all static views into the <code>public</code> folder</h4> <pre><code class="language-console">mv index.html about.html contact.html 404.html public/ </code></pre> <h4 id="heading-update-the-server-to-retrieve-static-views-from-the-public-folder">Update the server to retrieve static views from the <code>public</code> folder</h4> <p><code>server.js</code></p> <pre><code class="language-javascript">import express from "express"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; const app = express(); const host = "localhost"; const port = 3000; const __dirname = dirname(fileURLToPath(import.meta.url)); // Serve static assets (HTML, CSS, images) from the 'public' folder app.use(express.static(join(__dirname, "public"))); // Use the index.html static view as a response to 'GET /' requests app.get("/", (req, res) =&gt; { res.sendFile(join(__dirname, "/public/index.html")); }); // Use the about.html static view as a response to 'GET /about' requests app.get("/about", (req, res) =&gt; { res.sendFile(join(__dirname, "/public/about.html")); }); // Use the contact.html static view as a response to 'GET /contact' requests app.get("/contact", (req, res) =&gt; { res.sendFile(join(__dirname, "/public/contact.html")); }); // Use the 404.html static view as a response to unmatched requests app.use((req, res) =&gt; { res.status(404).sendFile(join(__dirname, "/public/404.html")); }); // Run the server on the specified port and hostname: app.listen(port, host, () =&gt; { console.log(`Server running live at http://${host}: ${port}`); }); </code></pre> <p><strong>Note</strong> The <code>app.use(express.static(join(__dirname, 'public')))</code> line tells Express to check the public folder first for static asset requests. If it finds the file there, it immediately serves it to the browser and stops the request-response cycle.</p> <p><strong>Example:</strong></p> <ul> <li><p>If a user requests <code>localhost:3000/about.html</code>, <code>express.static</code> checks the <code>public</code> folder for the <code>about.html</code> file. If found, Express automatically sends it without running the route handlers that follow the static middleware.</p> </li> <li><p>If a user asks for <code>localhost:3000/about</code>, <code>express.static</code> checks the <code>public</code> folder for an <code>about</code> file (no extension). If it finds an <code>about</code> directory instead, it redirects to <code>/about/</code>, where it looks for <code>index.html</code> by default. With the files created in this guide, neither an <code>about</code> file nor an <code>about</code> directory exists, so Express uses the <code>GET /about</code> custom route to handle the request.</p> </li> </ul> <p>Let's now discuss dynamic views.</p> <h3 id="heading-what-are-dynamic-views-in-expressjs">What Are Dynamic Views in Express.js?</h3> <p>Dynamic views contain placeholders that template engines, such as EJS, replace with data at runtime.</p> <p>Express.js supports template engines such as EJS, Pug, and Handlebars. Let's use EJS as an example to discuss how dynamic views work.</p> <h4 id="heading-what-is-ejs">What is EJS?</h4> <p>EJS (<a href="https://ejs.co">Embedded JavaScript templating</a>) is a templating language that lets you create dynamic HTML views by embedding JavaScript code within HTML templates using template tags.</p> <p>When the server renders a template, the EJS engine executes the embedded JavaScript and generates the final HTML sent to the client.</p> <p>For instance, consider the following code. Install EJS with <code>npm install ejs</code> before running this example:</p> <pre><code class="language-javascript">// Add the Express and EJS modules import express from "express"; import ejs from "ejs"; // Create a new Express application instance const app = express(); // Specify the host and port to receive requests const host = "localhost"; const port = 8000; // Define the data object const friendsArray = ["Sarah", "Abraham", "Mary"]; const replacementObject = { friends: friendsArray }; // Use backticks to define a template string with embedded JavaScript const templateString = ` &lt;h1&gt;Hello, &lt;%= friends.join(", "); %&gt;!&lt;/h1&gt; &lt;p&gt;Welcome to CodeSweetly.&lt;/p&gt; `; // Compile and render the template string to HTML (the rendered view) const html = ejs.render(templateString, replacementObject); // Use the dynamic view as a response to 'GET /' requests app.get("/", (req, res) =&gt; res.send(html)); // Run the server on the specified port and hostname app.listen(port, host, () =&gt; { console.log(`Server running live at http://${host}: ${port}!`); }); </code></pre> <p>The above snippet tells Express to send the rendered view to the client when users request the <code>/</code> path.</p> <p>At runtime, EJS evaluates <code>friends.join(", ")</code> using the array passed as <code>friends</code> and inserts the result, <code>Sarah, Abraham, Mary</code>, into the HTML. Here, rendering happens once when the script starts. Each request receives that same rendered HTML.</p> <h4 id="heading-what-are-ejs-template-tags">What are EJS template tags?</h4> <p>EJS template tags are special tags written using the <code>&lt;% ... %&gt;</code> syntax. They allow JavaScript code and values to be embedded into EJS view templates.</p> <p>When EJS renders the template, scriptlet tags execute JavaScript, output tags insert values, and comment tags are ignored.</p> <p>For example, the <code>&lt;%= %&gt;</code> tags below allow you to embed a JavaScript <code>friend</code> variable into the view template.</p> <pre><code class="language-ejs">&lt;h1&gt;About &lt;%= friend %&gt;, my special pal&lt;/h1&gt; </code></pre> <p><strong>Note:</strong> Output tags such as <code>&lt;%= %&gt;</code> must contain a valid JavaScript expression. Scriptlet tags can contain JavaScript statements and parts of control-flow structures, while comment tags contain comments.</p> <p><strong>Example:</strong></p> <ul> <li><p><code>&lt;%= &lt;p&gt;I love CodeSweetly&lt;/p&gt; %&gt;</code> will throw an error because <code>&lt;p&gt;I love CodeSweetly&lt;/p&gt;</code> is HTML, not JavaScript.</p> </li> <li><p><code>&lt;%= "&lt;p&gt;I love CodeSweetly&lt;/p&gt;" %&gt;</code> is valid because <code>"&lt;p&gt;I love CodeSweetly&lt;/p&gt;"</code> is embedded as a JavaScript string data type.</p> </li> </ul> <h4 id="heading-types-of-ejs-template-tags">Types of EJS template tags</h4> <p>The nine EJS tag forms covered here are as follows:</p> <p><strong>Tip:</strong> Wrap JavaScript code in EJS tags to distinguish it from the surrounding template content. A single pair of tags can contain multiple lines of JavaScript.</p> <h5 id="heading-closing-tag-gt">Closing tag (<code>%&gt;</code>)</h5> <p>Use the closing tag to end an EJS tag. It has no special behavior on its own.</p> <h5 id="heading-scriptlet-tag-lt">Scriptlet tag (<code>&lt;%</code>)</h5> <p>Use the scriptlet tag to run JavaScript code without outputting anything into the generated HTML. This tag is commonly used for control flow, such as <code>if</code>, <code>for</code>, and <code>forEach</code> logic.</p> <pre><code class="language-ejs">&lt;ul&gt; &lt;% friends.forEach(function () { %&gt; &lt;li&gt;My friend&lt;/li&gt; &lt;% }) %&gt; &lt;/ul&gt; </code></pre> <p>The snippet above wraps <code>&lt;% %&gt;</code> around the control-flow syntax to indicate to EJS that the JavaScript code is only for logic and should not be rendered to the generated HTML.</p> <h5 id="heading-escaped-output-tag-lt">Escaped output tag (<code>&lt;%=</code>)</h5> <p>Use the escaped output tag to evaluate JavaScript expressions and output the result into the generated HTML while escaping HTML characters. This tag helps prevent <a href="https://www.utep.edu/information-resources/iso/security-awareness/technical-security-resources/what-is-html-injection.html">HTML injection</a>.</p> <pre><code class="language-ejs">&lt;%= 100 + 200 %&gt; </code></pre> <p>The above snippet wraps <code>&lt;%= %&gt;</code> around the arithmetic expression to tell EJS to render the code's value into the generated HTML.</p> <p><strong>Note:</strong> If the expression evaluates to a string containing HTML, <code>&lt;%= %&gt;</code> escapes the HTML syntax so the browser displays it as text. For example, <code>&lt;%= "&lt;h1&gt;About CodeSweetly&lt;/h1&gt;" %&gt;</code> will render <code>&lt;h1&gt;About CodeSweetly&lt;/h1&gt;</code> as text, not an actual <code>&lt;h1&gt;</code> element.</p> <h5 id="heading-unescaped-output-tag-lt">Unescaped output tag (<code>&lt;%-</code>)</h5> <p>Use the unescaped tag to evaluate JavaScript expressions and output the result into the generated HTML without escaping HTML characters.</p> <pre><code class="language-ejs">&lt;%- "&lt;h1&gt;About CodeSweetly&lt;/h1&gt;" %&gt; </code></pre> <p>The snippet above wraps <code>&lt;%- %&gt;</code> around a string containing an <code>&lt;h1&gt;</code> element so the browser renders it as HTML. Use unescaped output only for trusted HTML.</p> <h5 id="heading-literal-opening-tag-lt">Literal opening tag (<code>&lt;%%</code>)</h5> <p>Use the literal tag to output a literal <code>&lt;%</code> sequence instead of treating it as an EJS tag. This is useful when you want to show EJS syntax in the generated output.</p> <pre><code class="language-ejs">&lt;%% "&lt;p&gt;&lt;strong&gt;Name:&lt;/strong&gt; &lt;em&gt;Oluwatobi&lt;/em&gt;&lt;/p&gt;" %&gt; </code></pre> <p>The snippet above outputs a literal <code>&lt;%</code> followed by the remaining text, without evaluating it as JavaScript. The generated HTML source is:</p> <pre><code class="language-plaintext">&lt;% " &lt;p&gt;&lt;strong&gt;Name:&lt;/strong&gt; &lt;em&gt;Oluwatobi&lt;/em&gt;&lt;/p&gt; " %&gt; </code></pre> <p>The HTML tags remain unescaped, so this shows the generated source, not how the browser displays the page.</p> <h5 id="heading-newline-trimmed-ending-tag-gt">Newline-trimmed ending tag (<code>-%&gt;</code>)</h5> <p>Use the newline-trimmed ending tag to remove the newline immediately after the closing tag.</p> <pre><code class="language-ejs">&lt;ul&gt; &lt;% friends.forEach(function (friend) { %&gt; &lt;li&gt;&lt;%= friend -%&gt;&lt;/li&gt; &lt;% }) %&gt; &lt;/ul&gt; </code></pre> <p>The snippet above wraps <code>&lt;%= -%&gt;</code> around the <code>friend</code> variable to tell EJS to trim any newline following the closing EJS tag.</p> <p><strong>Tip:</strong> The newline-trimmed ending tag is also called the newline slurp tag.</p> <h5 id="heading-whitespace-slurp-opening-tag-lt">Whitespace slurp opening tag (<code>&lt;%_</code>)</h5> <p>Use the whitespace slurping tag to remove spaces and tabs immediately before the opening EJS tag on the same line.</p> <pre><code class="language-ejs">&lt;ul&gt; &lt;%_ friends.forEach(function (friend) { %&gt; &lt;li&gt;&lt;%= friend %&gt;&lt;/li&gt; &lt;%_ }) %&gt; &lt;/ul&gt; </code></pre> <p>The snippet above wraps <code>&lt;%_ %&gt;</code> around the control-flow syntax to tell EJS to trim the indentation before the template tags.</p> <h5 id="heading-whitespace-slurp-closing-tag-gt">Whitespace slurp closing tag (<code>_%&gt;</code>)</h5> <p>Use the whitespace slurping ending tag to remove spaces and tabs immediately after the closing tag, followed by one newline if present.</p> <pre><code class="language-ejs">&lt;ul&gt; &lt;% friends.forEach(function (friend) { _%&gt; &lt;li&gt;&lt;%= friend %&gt;&lt;/li&gt; &lt;% }) _%&gt; &lt;/ul&gt; </code></pre> <p>The snippet above wraps <code>&lt;% _%&gt;</code> around the control-flow syntax to tell EJS to trim the newline after each closing tag. The indentation on the following line remains.</p> <h5 id="heading-comment-tag-lt">Comment tag (<code>&lt;%#</code>)</h5> <p>Use the comment tag to add EJS comments that are ignored during rendering and do not appear in the generated HTML.</p> <pre><code class="language-ejs">&lt;ul&gt; &lt;%# Loop through the friends array %&gt; &lt;% friends.forEach(function (friend) { _%&gt; &lt;li&gt;&lt;%= friend %&gt;&lt;/li&gt; &lt;% }) _%&gt; &lt;/ul&gt; </code></pre> <p>The snippet above wraps the comment in <code>&lt;%# %&gt;</code>.</p> <p><strong>Tip:</strong> EJS allows you to combine some tags. For example, the following combinations are possible:</p> <ul> <li><p><code>&lt;%_ _%&gt;</code>: Combine the whitespace slurping scriptlet and ending tag to remove adjacent spaces and tabs on the same line, plus one following newline if present.</p> </li> <li><p><code>&lt;% -%&gt;</code>: Combine the control-flow with the newline slurp.</p> </li> <li><p><code>&lt;%= -%&gt;</code>: Combine the escaped tag with the newline slurp.</p> </li> </ul> <h4 id="heading-how-to-use-ejs-in-an-express-project">How to use EJS in an Express project</h4> <p>The following sections will guide you through the process of using EJS in your Express.js project.</p> <h5 id="heading-install-ejs">Install EJS</h5> <p>First, install EJS in your Express project.</p> <pre><code class="language-console">npm install ejs@6.0.1 </code></pre> <p>Next, open your project's <code>server.js</code> module and configure the web server to serve the appropriate dynamically generated HTML view in response to requests.</p> <p><code>server.js</code></p> <pre><code class="language-javascript">// Add the Express and EJS modules import express from "express"; import ejs from "ejs"; // Create a new Express application instance const app = express(); // Specify the host and port to receive requests const host = "localhost"; const port = 8000; // Define the data object const friendsData = { bestFriend: "Sarah", codingFriend: "Abraham", dreamFriend: "Mary", }; const replacementObject = { friends: friendsData }; // Use backticks to define a template string with embedded JavaScript const templateString = ` &lt;html&gt; &lt;body&gt; &lt;h1&gt;List of Friends&lt;/h1&gt; &lt;ul&gt; &lt;%# Loop through the friends object %&gt; &lt;% for (const eachFriend in friends) { -%&gt; &lt;li&gt;&lt;%= friends[eachFriend] %&gt; is my &lt;%= eachFriend %&gt;&lt;/li&gt; &lt;% } -%&gt; &lt;/ul&gt; &lt;/body&gt; &lt;/html&gt; `; // Compile and render the template string to HTML (the rendered view) const htmlData = ejs.render(templateString, replacementObject); // Use the dynamic view as a response to 'GET /' requests app.get("/", (req, res) =&gt; res.send(htmlData)); // Run the server on the specified port and hostname app.listen(port, host, () =&gt; { console.log(`Server running live at http://${host}: ${port}!`); }); </code></pre> <p>Here are the main things the snippet above does:</p> <ul> <li><p>Create an Express application instance to access Express APIs, such as routers and middleware, which simplify HTTP request handling in Node.js.</p> </li> <li><p>Render the template string once when the script starts to generate the HTML that the server sends to each client.</p> </li> <li><p>Create routes to receive and respond to clients' requests.</p> </li> <li><p>Use Express's <code>app.listen()</code> method to start the Node.js HTTP server on a specific port and execute a callback when the server begins listening for client requests.</p> </li> </ul> <h5 id="heading-run-the-express-app">Run the Express app</h5> <p>After setting up the server, run it with Node.js.</p> <pre><code class="language-console">node --watch server.js </code></pre> <p>Afterward, check your app running live at <code>http://localhost:8000</code>.</p> <p><strong>Tip:</strong> Stop the running process using <code>Ctrl + C</code> on Windows, macOS, or Linux.</p> <p>To keep things organized, put all your view templates in a <code>views</code> directory. This is a common practice.</p> <h5 id="heading-create-a-directory-for-view-templates">Create a directory for view templates</h5> <p>Create a <code>views</code> directory at your project's root to store all view templates.</p> <pre><code class="language-console">mkdir views </code></pre> <h5 id="heading-create-an-index-view-template">Create an index view template</h5> <pre><code class="language-console">touch views/index.ejs </code></pre> <p><strong>Tip:</strong> EJS template files use the <code>.ejs</code> extension.</p> <p>Open the file and move the HTML template from <code>server.js</code> into it.</p> <p><code>views/index.ejs</code></p> <pre><code class="language-ejs">&lt;html&gt; &lt;body&gt; &lt;h1&gt;List of Friends&lt;/h1&gt; &lt;ul&gt; &lt;%# Loop through the friends object %&gt; &lt;% for (const eachFriend in friends) { -%&gt; &lt;li&gt;&lt;%= friends[eachFriend] %&gt; is my &lt;%= eachFriend %&gt;&lt;/li&gt; &lt;% } -%&gt; &lt;/ul&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <h5 id="heading-update-the-server-to-retrieve-view-templates-from-the-views-folder">Update the server to retrieve view templates from the <code>views</code> folder</h5> <p><code>server.js</code></p> <pre><code class="language-javascript">import express from "express"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; const app = express(); const host = "localhost"; const port = 8000; const __dirname = dirname(fileURLToPath(import.meta.url)); // Specify the app's views directory app.set("views", join(__dirname, "views")); // Specify the app's view engine app.set("view engine", "ejs"); // Define the data object const friendsData = { bestFriend: "Sarah", codingFriend: "Abraham", dreamFriend: "Mary", }; const replacementObject = { friends: friendsData }; // Compile and render the index view template to HTML and use it as a response to 'GET /' requests app.get("/", (req, res) =&gt; { res.render("index", replacementObject); }); // Run the server on the specified port and hostname app.listen(port, host, () =&gt; { console.log(`Server running live at http://${host}: ${port}`); }); </code></pre> <p>The snippet above uses Express's <code>render()</code> method to generate HTML from the <code>index.ejs</code> template file.</p> <p><strong>Note:</strong></p> <ul> <li>If you omit the <code>app.set("view engine", "ejs")</code> line, then the <code>render()</code> method's view template argument must include a file extension like this:</li> </ul> <pre><code class="language-javascript">app.get("/", (req, res) =&gt; { res.render("index.ejs", replacementObject); }); </code></pre> <ul> <li><p>Express has no default template engine. Express also supports engines such as Pug (formerly called Jade), EJS, Mustache, and Handlebars.</p> </li> <li><p><code>app.set()</code> is the method for adding a key-value pair to the application's settings. You can retrieve the setting's value with <code>app.get()</code> as follows:</p> </li> </ul> <pre><code class="language-javascript">app.set("my name", "Oluwatobi"); const bio = app.get("my name"); console.log(bio); // Outputs: "Oluwatobi" </code></pre> <h4 id="heading-how-to-create-reusable-ejs-templates-partials">How to create reusable EJS templates (partials)</h4> <p>EJS provides the <code>include()</code> method for including (nesting) one view template into another.</p> <p><strong>Tip:</strong> In EJS, partials are reusable templates.</p> <h5 id="heading-syntax-of-the-include-method">Syntax of the include() method</h5> <p>The <code>include()</code> method accepts two arguments. Here's the syntax:</p> <pre><code class="language-ejs">&lt;%- include("path/to/partial", data) %&gt; </code></pre> <ul> <li><p><code>"path/to/partial"</code>: (required) The path to the reusable template, relative to the current file (the parent template). For example, if the current file is at <code>"./views/index.ejs"</code> and the partial is at <code>"./views/partials/footer.ejs"</code>, the <code>include()</code> path argument would be <code>"partials/footer"</code>.</p> </li> <li><p><code>data</code>: (optional) An object containing the properties to pass to the partial.</p> </li> </ul> <h5 id="heading-how-to-include-a-partial-in-a-view-template">How to include a partial in a view template</h5> <p>Consider the following server file:</p> <p><code>server.js</code></p> <pre><code class="language-javascript">import express from "express"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; const app = express(); const host = "localhost"; const port = 8080; const __dirname = dirname(fileURLToPath(import.meta.url)); app.set("views", join(__dirname, "views")); app.set("view engine", "ejs"); const friendsData = { bestFriend: "Sarah", codingFriend: "Abraham", dreamFriend: "Mary", }; const replacementObject = { friends: friendsData }; app.get("/", (req, res) =&gt; { res.render("index", replacementObject); }); app.listen(port, host, () =&gt; { console.log(`Server running live at http://${host}: ${port}`); }); </code></pre> <p>The snippet above will render an <code>index</code> template when users send a GET request to the <code>"/"</code> path. Below is the <code>index.ejs</code> template file.</p> <p><code>views/index.ejs</code></p> <pre><code class="language-ejs">&lt;html&gt; &lt;body&gt; &lt;h1&gt;List of Friends&lt;/h1&gt; &lt;ul&gt; &lt;%# Loop through the friends object %&gt; &lt;% for (const eachFriend in friends) { -%&gt; &lt;li&gt;&lt;%= friends[eachFriend] %&gt; is my &lt;%= eachFriend %&gt;&lt;/li&gt; &lt;% } -%&gt; &lt;/ul&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>If you need the <code>&lt;li&gt;</code> element to be reusable in multiple templates, extract it into a separate file and use the <code>include()</code> method to add it to any template as needed. Here's how:</p> <h5 id="heading-create-a-partials-directory">Create a partials directory</h5> <p>Create a <code>partials</code> directory in your <code>views</code> folder to store all reusable view templates.</p> <pre><code class="language-console">mkdir views/partials </code></pre> <h5 id="heading-create-a-partial-view-template">Create a partial view template</h5> <p>Create a partial view template for the <code>&lt;li&gt;</code> element.</p> <pre><code class="language-console">touch views/partials/friendLi.ejs </code></pre> <p>Open the file and add the <code>&lt;li&gt;</code> element.</p> <p><code>views/partials/friendLi.ejs</code></p> <pre><code class="language-ejs">&lt;li&gt;&lt;%= friend %&gt; is my &lt;%= friendType %&gt;&lt;/li&gt; </code></pre> <h5 id="heading-update-the-main-view-template">Update the main view template</h5> <p>Open the <code>index.ejs</code> file and use the <code>include()</code> method to add the <code>friendLi.ejs</code> partial view template.</p> <p><code>views/index.ejs</code></p> <pre><code class="language-ejs">&lt;html&gt; &lt;body&gt; &lt;h1&gt;List of Friends&lt;/h1&gt; &lt;ul&gt; &lt;%# Loop through the friends object %&gt; &lt;% for (const eachFriend in friends) { -%&gt; &lt;%- include("partials/friendLi", { friend: friends[eachFriend], friendType: eachFriend }) %&gt; &lt;% } -%&gt; &lt;/ul&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>Using <code>include()</code> to add the <code>&lt;li&gt;</code> element makes it reusable in multiple templates, rather than restricting it to the <code>index.ejs</code> view template.</p> <h5 id="heading-run-your-server-from-the-root-directory">Run your server from the root directory</h5> <pre><code class="language-console">node --watch server.js </code></pre> <p>Then, check your app running live at <code>http://localhost:8080</code>.</p> <p><strong>Note:</strong> Although the steps above demonstrate EJS partials with a simple <code>&lt;li&gt;</code> element, you can use the same concept to reuse your application's navbar, footer, and other components across multiple view templates.</p> <h2 id="heading-overview">Overview</h2> <p>In this handbook, we explored the core concepts you need to start building applications with Node.js and Express.js. We discussed how Node.js works, its module support, and how to create HTTP servers. We also explored how Express.js simplifies server-side development through routes, routers, and views.</p> <p>Whether you’re considering a small personal project or a full-stack application for a larger user base, you now have a solid starting point for building with Node.js and Express.js.</p> <p>Thanks for reading!</p> <h3 id="heading-dive-deeper-into-nodejs-and-expressjs">Dive Deeper into Node.js and Express.js</h3> <p>This handbook has given you a peek inside my <a href="https://www.amazon.com/dp/B0HK23L2Y1?tag=codesweetly00-20">Node.js and Express.js Simplified book</a>.</p> <p>Whether you’re learning backend development for the first time, expanding your JavaScript skills beyond frontend development, or looking for a practical introduction to Node.js and Express.js, the book will help you build on what you’ve learned here and develop the skills to create, test, and deploy complete applications.</p> <p>You’ll dive deeper into Node.js and Express.js through practical explanations and hands-on examples covering topics such as file management, file uploads, forms, sessions, frontend-to-backend communication, testing, TypeScript, and deployment.</p> <p><a href="https://www.amazon.com/dp/B0HK23L2Y1?tag=codesweetly00-20"><img src="https://cdn.hashnode.com/uploads/covers/61672d1653401f641ba159b4/bcad7d15-53d7-4ba7-8974-b424c50c01a0.jpg" alt="Build with Node.js &amp; Express.js: A practical, beginner-friendly guide to building, testing, and deploying web applications" style="display: block;" width="600" height="400" loading="lazy"></a></p>

How to apply

  1. 1 Read the full details above and confirm you meet the eligibility criteria.
  2. 2 Prepare your documents — an updated CV, and any cover letter, proposal or certificates required.
  3. 3 Click Apply on official site to complete your application on Oluwatobi Sofela’s official page.
  4. 4 Submit as early as possible — many close once filled.
Apply on official site

Sourced from freecodecamp. Always verify details on the official website. Opportunihub never charges you to apply.

Frequently asked questions

How do I apply for The Node.js and Express.js Handbook for Beginners – Servers, Routes, Routers, and Views Explained?

Review the full details and eligibility on this page, prepare your documents, then use the “Apply on official site” button to complete your application on Oluwatobi Sofela’s official page.

Is this opportunity remote or location-based?

This opportunity is remote-friendly and open to applicants who can work from anywhere.

Is The Node.js and Express.js Handbook for Beginners – Servers, Routes, Routers, and Views Explained free to apply for?

Opportunihub lists this Course for free. Legitimate Courses do not ask for payment to apply — never pay a fee to submit an application.