Introduction to Node.js & Express.js
This comprehensive guide introduces beginners to Node.js and Express.js, covering setup, core features, routing, middleware, database integration, and deployment.
Welcome to the world of server-side JavaScript development with Node.js and Express.js! If you're familiar with JavaScript but have never dipped into server-side development, this guide is for you. We'll embark on a journey to explore the key features of Node.js and Express.js, how to set up your environment, write your first server, and gradually build up to more advanced topics. Let's get started!
What is Node.js?
Node.js is a powerful, open-source, cross-platform JavaScript runtime environment that executes JavaScript code server-side. Originally created by Ryan Dahl in 2009, Node.js has become a cornerstone technology for building scalable network applications.
History and Purpose
Node.js was born out of the need for a simpler way to build scalable web servers. Traditionally, server-side scripting was done using languages like PHP, Ruby, and Python. However, JavaScript, previously confined to browsers, was gaining popularity among developers due to its simplicity and versatility. Node.js leverages the V8 engine (the same engine used by Google Chrome) to run JavaScript outside the browser, thus enabling the same language to be used both on the client-side and server-side.
Key Features
- Non-blocking, event-driven architecture: Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications.
- Asynchronous operations: Node.js is built around asynchronous programming, which means you can perform I/O operations without blocking the main thread.
- Single-threaded but highly scalable: Despite being single-threaded, Node.js applications can handle thousands of concurrent connections efficiently.
- Rich ecosystem and community: Node.js has a massive community and a rich ecosystem of packages available through npm (Node Package Manager).
Use Cases
- Real-time applications: Node.js is ideal for applications requiring real-time communication, such as chat applications and multiplayer games.
- Microservices architecture: Its lightweight nature makes it perfect for building microservices.
- Single-page applications (SPAs): Often used in conjunction with front-end frameworks like React, Angular, and Vue.
- APIs and microservices: Developers use Node.js to build APIs and microservices efficiently.
Setting Up Your Environment
Before we dive deep into writing code, we need to set up our environment. We'll install Node.js, which includes npm (Node Package Manager) by default. This setup process is straightforward and essential to get you started.
Installing Node.js
Downloading and Installing Node.js
- Visit the official Node.js website.
- Choose your operating system (Windows, macOS, or Linux).
- Download the LTS (Long-Term Support) version, which is recommended for most users.
- Follow the installation instructions for your specific operating system.
Verifying Installation
Once installed, you can verify that Node.js and npm are set up correctly by running the following commands in your terminal or command prompt:
node -v
npm -v
These commands should return the version numbers of Node.js and npm, respectively.
Setting Up the Workspace
Introduction to npm (Node Package Manager)
npm is the package manager for Node.js, enabling you to install and manage packages (libraries or modules) used in your project. It is installed automatically when you install Node.js.
Creating a Node.js Project
To create a new Node.js project, follow these steps:
-
Open your terminal or command prompt.
-
Create a new directory for your project:
mkdir my-node-app
-
Navigate to the new directory:
cd my-node-app
-
Initialize a new Node.js project:
npm init -y
The
-y
flag auto-answers "yes" to all prompts, generating a defaultpackage.json
file for your project.
Getting Started with Node.js
Now that our environment is set up, it’s time to write our first Node.js program.
Writing Your First Node.js Program
Basic Structure
Let's create a simple Node.js application that just says "Hello, World!" to the console.
-
Create a new file named
index.js
in your project directory. -
Open
index.js
in your favorite code editor and add the following code:console.log('Hello, World!');
-
Save the file.
Running the Program
To run the program, execute the following command in your terminal:
node index.js
You should see the output:
Hello, World!
Asynchronous Nature of Node.js
Node.js is known for its non-blocking, event-driven architecture, which makes it ideal for I/O-bound applications. Let's explore this concept through callback functions, promises, and async/await.
Callback Functions
Callbacks are functions passed as arguments to other functions and executed after some operation has been completed. They are the traditional way to handle asynchronous operations in Node.js.
Here's an example using a callback function to read a file asynchronously:
-
Create a file named
example.txt
in your project directory and add the following text to it:Welcome to Node.js!
-
Modify
index.js
to read the contents ofexample.txt
using the built-infs
module (File System) and a callback function:const fs = require('fs'); fs.readFile('example.txt', 'utf8', (err, data) => { if (err) { console.error('Error reading file:', err); return; } console.log(data); });
-
Run the program:
node index.js
You should see the output:
Welcome to Node.js!
In this example,
fs.readFile
is an asynchronous function that reads the content ofexample.txt
. The callback function is executed once the file is read, either successfully or with an error.
Promises
Promises provide a more modern way to handle asynchronous operations compared to callbacks. A promise represents a value that may not be available yet but will be at some point in the future.
Modify index.js
to use a promise with fs.promises.readFile
to read the contents of example.txt
:
const fs = require('fs').promises;
fs.promises.readFile('example.txt', 'utf8')
.then(data => console.log(data))
.catch(err => console.error('Error reading file:', err));
Run the program:
node index.js
You should see the same output:
Welcome to Node.js!
In this example, fs.promises.readFile
returns a promise. We use the .then
method to handle the result and the .catch
method to handle any errors.
Async/Await
Async/await is the most recent addition to JavaScript for handling asynchronous operations, providing a cleaner and more readable syntax compared to promises and callbacks.
Modify index.js
to use async/await
for reading the contents of example.txt
:
const fs = require('fs').promises;
async function readFile() {
try {
const data = await fs.readFile('example.txt', 'utf8');
console.log(data);
} catch (err) {
console.error('Error reading file:', err);
}
}
readFile();
Run the program:
node index.js
You should see the same output:
Welcome to Node.js!
In this example, the readFile
function is declared as an async
function, allowing the use of await
to pause the execution until the promise is resolved. The try/catch
block is used to handle any errors.
Introduction to Express.js
Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. It is widely used in the JavaScript ecosystem and is regarded as one of the most popular server frameworks in Node.js.
Overview of Express.js
Express.js simplifies the process of setting up a server by providing a set of tools and middleware, allowing you to easily handle routing, middleware, and more.
Key Features
- Routing: Easily set up routes to handle requests.
- Middleware: Use middleware to modify the incoming request or outgoing response.
- Templating Engines: Integrate various templating engines like EJS, Pug, and Handlebars.
- Error Handling: Implement custom error handling for robust applications.
Use Cases
- Web applications: Build RESTful APIs and web applications.
- Microservices: Develop microservices with the help of Express.js.
- Single-page applications (SPAs): Work seamlessly with front-end frameworks like React, Angular, and Vue.js.
- Serverless functions: Deploy small, isolated functions using events triggered through HTTP requests.
Setting Up Express.js
Initializing Express Application
To set up an Express.js application, you need to install the Express.js package using npm.
-
Create a new directory for your Express.js project and navigate to it:
mkdir my-express-app cd my-express-app
-
Initialize a new Node.js project:
npm init -y
-
Install Express.js:
npm install express
Basic Server Setup
Create a new file named app.js
and set up a basic Express server:
-
Open
app.js
in your code editor and add the following code:const express = require('express'); const app = express(); const PORT = 3000; app.get('/', (req, res) => { res.send('Welcome to Express.js!'); }); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000
. You should see the message:Welcome to Express.js!
Middleware in Express.js
Middleware functions are functions that have access to the request object (req
), the response object (res
), and the next middleware function in the application’s request-response cycle. They are extremely useful for handling requests, responses, modifying response objects, and much more.
Built-in Middleware
Express.js comes with several built-in middleware functions, such as express.static
for serving static files and express.json
for parsing JSON data.
Third-party Middleware
There are numerous third-party middleware available, such as morgan
for logging HTTP requests, body-parser
for parsing request bodies, and helmet
for setting HTTP headers.
Custom Middleware
You can also write your custom middleware functions. Let's create a simple custom middleware that logs each request to the console.
-
Modify
app.js
to include a custom middleware function:const express = require('express'); const app = express(); const PORT = 3000; // Custom middleware function app.use((req, res, next) => { console.log(`${req.method} request for '${req.url}' - ${new Date()}`); next(); // Pass control to the next middleware function }); app.get('/', (req, res) => { res.send('Welcome to Express.js!'); }); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000
. You should see the same message, but in your terminal, you'll see logs for each request:GET request for '/' - 2023-10-01T10:00:00.000Z
Routing with Express.js
Routing refers to determining how an application responds to a client request to a specific endpoint (URI) and HTTP method (GET, POST, etc.). Let’s explore basic and advanced routing techniques.
Basic Routing
Route Methods
Route methods correspond to HTTP methods such as GET
, POST
, PUT
, DELETE
, etc.
Let's add a few routes to our Express application:
-
Modify
app.js
to include additional routes:const express = require('express'); const app = express(); const PORT = 3000; // Custom middleware function app.use((req, res, next) => { console.log(`${req.method} request for '${req.url}' - ${new Date()}`); next(); }); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.send('Welcome to Express.js!'); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000/about
andhttp://localhost:3000/contact
. You should see the respective pages.
Route Parameters
Route parameters are named URL segments that are captured and passed as part of req.params
object.
Let's modify our application to include a dynamic route parameter for user IDs:
-
Add a new route to handle user profiles:
const express = require('express'); const app = express(); const PORT = 3000; // Custom middleware function app.use((req, res, next) => { console.log(`${req.method} request for '${req.url}' - ${new Date()}`); next(); }); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.send('Welcome to Express.js!'); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Route to handle GET requests with dynamic user ID parameter app.get('/user/:id', (req, res) => { res.send(`User ID: ${req.params.id}`); }); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000/user/123
. You should see the message:User ID: 123
Query Parameters
Query parameters are key-value pairs sent in the URL after a question mark (?
) and are used to pass additional data to the server.
Let's modify our application to handle query parameters:
-
Add a new route to handle queries:
const express = require('express'); const app = express(); const PORT = 3000; // Custom middleware function app.use((req, res, next) => { console.log(`${req.method} request for '${req.url}' - ${new Date()}`); next(); }); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.send('Welcome to Express.js!'); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Route to handle GET requests with dynamic user ID parameter app.get('/user/:id', (req, res) => { res.send(`User ID: ${req.params.id}`); }); // Route to handle GET requests with query parameters app.get('/search', (req, res) => { const query = req.query; res.send(`You searched for: ${query.q}`); }); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000/search?q=nodejs
. You should see the message:You searched for: nodejs
Advanced Routing
Route Grouping
Route grouping helps organize routes into logical groups and is especially useful in large applications with many routes.
Let's group routes related to users:
-
Create a new file
routes/userRoutes.js
:const express = require('express'); const router = express.Router(); // Route to handle GET requests to /user router.get('/', (req, res) => { res.send('User List'); }); // Route to handle GET requests to /user/:id router.get('/:id', (req, res) => { res.send(`User ID: ${req.params.id}`); }); module.exports = router;
-
Modify
app.js
to use the user routes:const express = require('express'); const userRoutes = require('./routes/userRoutes'); const app = express(); const PORT = 3000; // Custom middleware function app.use((req, res, next) => { console.log(`${req.method} request for '${req.url}' - ${new Date()}`); next(); }); // Use user routes app.use('/user', userRoutes); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.send('Welcome to Express.js!'); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Route to handle GET requests with query parameters app.get('/search', (req, res) => { const query = req.query; res.send(`You searched for: ${query.q}`); }); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000/user
andhttp://localhost:3000/user/123
. You should see the respective pages.
Route Handlers
You can specify functions to handle different HTTP requests, making your code modular and easier to manage.
-
Modify
routes/userRoutes.js
to include additional route handlers:const express = require('express'); const router = express.Router(); // Route to handle GET requests to /user router.get('/', (req, res) => { res.send('User List'); }); // Route to handle GET requests to /user/:id router.get('/:id', (req, res) => { res.send(`User ID: ${req.params.id}`); }); // Route to handle POST requests to /user router.post('/', (req, res) => { res.send('User created successfully'); }); // Route to handle PUT requests to /user/:id router.put('/:id', (req, res) => { res.send(`User ID ${req.params.id} updated successfully`); }); // Route to handle DELETE requests to /user/:id router.delete('/:id', (req, res) => { res.send(`User ID ${req.params.id} deleted successfully`); }); module.exports = router;
-
Make sure
app.js
is set up to use the user routes:const express = require('express'); const userRoutes = require('./routes/userRoutes'); const app = express(); const PORT = 3000; // Custom middleware function app.use((req, res, next) => { console.log(`${req.method} request for '${req.url}' - ${new Date()}`); next(); }); // Use user routes app.use('/user', userRoutes); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.send('Welcome to Express.js!'); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Route to handle GET requests with query parameters app.get('/search', (req, res) => { const query = req.query; res.send(`You searched for: ${query.q}`); }); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.js
-
Use tools like Postman or curl to test different HTTP methods:
- GET request to
http://localhost:3000/user
:User List
- GET request to
http://localhost:3000/user/123
:User ID: 123
- POST request to
http://localhost:3000/user
:User created successfully
- PUT request to
http://localhost:3000/user/123
:User ID 123 updated successfully
- DELETE request to
http://localhost:3000/user/123
:User ID 123 deleted successfully
- GET request to
Working with HTTP Methods
HTTP methods are actions that specify what you want to do with a requested resource. We've already seen examples of GET
, POST
, PUT
, and DELETE
methods.
GET Requests
GET requests are used to retrieve data from a server. In our previous examples, we used GET
requests to display different pages.
POST Requests
POST requests are used to send data to a server to create/update a resource.
Modify routes/userRoutes.js
to handle a POST request:
-
Modify the file to include a POST request:
const express = require('express'); const router = express.Router(); // Route to handle GET requests to /user router.get('/', (req, res) => { res.send('User List'); }); // Route to handle GET requests to /user/:id router.get('/:id', (req, res) => { res.send(`User ID: ${req.params.id}`); }); // Route to handle POST requests to /user router.post('/', (req, res) => { res.send('User created successfully'); }); // Route to handle PUT requests to /user/:id router.put('/:id', (req, res) => { res.send(`User ID ${req.params.id} updated successfully`); }); // Route to handle DELETE requests to /user/:id router.delete('/:id', (req, res) => { res.send(`User ID ${req.params.id} deleted successfully`); }); module.exports = router;
-
Run the server:
node app.js
-
Use Postman or curl to send a POST request to
http://localhost:3000/user
:POST http://localhost:3000/user
You should receive:
User created successfully
PUT Requests
PUT requests are used to update a resource. In our example, we've already handled PUT requests for user routes.
DELETE Requests
DELETE requests are used to delete a resource. Our example also includes DELETE requests for user routes.
Handling JSON Data
JSON (JavaScript Object Notation) is a lightweight data interchange format that is easy to read and write for humans and easy to parse and generate for machines.
Parsing JSON Data
To work with JSON data in Express.js, you need to use middleware like express.json()
to parse incoming request bodies.
Example: Sending JSON in POST Request
-
Modify
routes/userRoutes.js
to parse JSON data:const express = require('express'); const router = express.Router(); // Middleware to parse JSON data router.use(express.json()); // Route to handle GET requests to /user router.get('/', (req, res) => { res.send('User List'); }); // Route to handle GET requests to /user/:id router.get('/:id', (req, res) => { res.send(`User ID: ${req.params.id}`); }); // Route to handle POST requests to /user router.post('/', (req, res) => { console.log(req.body); // Log the parsed JSON data res.send('User created successfully'); }); // Route to handle PUT requests to /user/:id router.put('/:id', (req, res) => { console.log(req.body); // Log the parsed JSON data res.send(`User ID ${req.params.id} updated successfully`); }); // Route to handle DELETE requests to /user/:id router.delete('/:id', (req, res) => { res.send(`User ID ${req.params.id} deleted successfully`); }); module.exports = router;
-
Run the server:
node app.js
-
Use Postman or curl to send a POST request with JSON data to
http://localhost:3000/user
:POST http://localhost:3000/user Content-Type: application/json { "name": "John Doe", "email": "johndoe@example.com" }
You should see the following output in the terminal:
{ name: 'John Doe', email: 'johndoe@example.com' }
And the response in the browser:
User created successfully
Templating Engines with Express.js
Templating engines enable you to generate HTML markup dynamically using templates.
Introduction to Templating Engines
Express.js supports several templating engines, such as EJS, Pug, and Handlebars. Let's explore EJS as an example.
EJS (Embedded JavaScript)
EJS is a simple templating engine for Node.js and Express.js that allows you to embed JavaScript code in HTML.
-
Install EJS:
npm install ejs
-
Set up EJS in
app.js
:const express = require('express'); const userRoutes = require('./routes/userRoutes'); const app = express(); const PORT = 3000; // Set the view engine to EJS app.set('view engine', 'ejs'); // Middleware to parse JSON data router.use(express.json()); // Route to handle GET requests to /user router.get('/', (req, res) => { res.send('User List'); }); // Route to handle GET requests to /user/:id router.get('/:id', (req, res) => { res.send(`User ID: ${req.params.id}`); }); // Route to handle POST requests to /user router.post('/', (req, res) => { console.log(req.body); // Log the parsed JSON data res.send('User created successfully'); }); // Route to handle PUT requests to /user/:id router.put('/:id', (req, res) => { console.log(req.body); // Log the parsed JSON data res.send(`User ID ${req.params.id} updated successfully`); }); // Route to handle DELETE requests to /user/:id router.delete('/:id', (req, res) => { res.send(`User ID ${req.params.id} deleted successfully`); }); module.exports = router;
-
Create a new directory named
views
in your project directory. Inside theviews
directory, create a file namedindex.ejs
:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Home</title> </head> <body> <h1>Welcome to Express.js with EJS!</h1> </body> </html>
-
Modify
app.js
to render theindex.ejs
template for the home route:const express = require('express'); const userRoutes = require('./routes/userRoutes'); const app = express(); const PORT = 3000; // Set the view engine to EJS app.set('view engine', 'ejs'); // Middleware to parse JSON data app.use(express.json()); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.render('index'); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Use user routes app.use('/user', userRoutes); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000
. You should see the renderedindex.ejs
template.
Pug (Formerly Jade)
Pug is a high-performance template engine that compiles to HTML. It is popular for its readability and minimal syntax.
Let's integrate Pug into our application.
-
Install Pug:
npm install pug
-
Set up Pug in
app.js
:const express = require('express'); const userRoutes = require('./routes/userRoutes'); const app = express(); const PORT = 3000; // Set the view engine to Pug app.set('view engine', 'pug'); // Middleware to parse JSON data app.use(express.json()); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.render('index'); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Use user routes app.use('/user', userRoutes); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Create a new file named
index.pug
in theviews
directory:doctype html html head meta(charset='UTF-8') meta(name='viewport' content='width=device-width, initial-scale=1.0') title Home body h1 Welcome to Express.js with Pug!
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000
. You should see the renderedindex.pug
template.
Handlebars
Handlebars is another popular templating engine that is easy to learn and use.
Let's integrate Handlebars into our application.
-
Install Handlebars:
npm install express-handlebars
-
Set up Handlebars in
app.js
:const express = require('express'); const exphbs = require('express-handlebars'); const userRoutes = require('./routes/userRoutes'); const app = express(); const PORT = 3000; // Set the view engine to Handlebars app.engine('handlebars', exphbs()); app.set('view engine', 'handlebars'); // Middleware to parse JSON data app.use(express.json()); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.render('index'); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Use user routes app.use('/user', userRoutes); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Create a new file named
index.handlebars
in theviews
directory:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Home</title> </head> <body> <h1>Welcome to Express.js with Handlebars!</h1> </body> </html>
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000
. You should see the renderedindex.handlebars
template.
Error Handling in Express.js
Error handling in Express.js is crucial for providing meaningful error messages and maintaining application stability.
Basic Error Handling
Express.js allows you to define catch-all handlers to handle errors globally.
Catch-all Handler
Add a catch-all handler at the end of your routes to handle any uncaught errors:
-
Modify
app.js
to include a catch-all error handler:const express = require('express'); const exphbs = require('express-handlebars'); const userRoutes = require('./routes/userRoutes'); const app = express(); const PORT = 3000; // Set the view engine to Handlebars app.engine('handlebars', exphbs()); app.set('view engine', 'handlebars'); // Middleware to parse JSON data app.use(express.json()); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.render('index'); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Use user routes app.use('/user', userRoutes); // Catch-all error handler app.use((err, req, res, next) => { console.error(err.stack); res.status(500).send('Something broke!'); }); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.js
Custom Error Handling
You can create custom error handlers for different types of errors.
-
Create a new file named
errors.js
in your project directory and add the following code:module.exports = (err, req, res, next) => { console.error(err.stack); res.status(500).send('Something broke!'); };
-
Modify
app.js
to use the custom error handler:const express = require('express'); const exphbs = require('express-handlebars'); const userRoutes = require('./routes/userRoutes'); const errorHandler = require('./errors'); const app = express(); const PORT = 3000; // Set the view engine to Handlebars app.engine('handlebars', exphbs()); app.set('view engine', 'handlebars'); // Middleware to parse JSON data app.use(express.json()); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.render('index'); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Use user routes app.use('/user', userRoutes); // Custom error handler app.use(errorHandler); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.js
Middleware in Express.js
Error Middleware
To handle errors, we can create custom error middleware functions.
-
Modify
errors.js
to handle specific types of errors:module.exports = (err, req, res, next) => { console.error(err.stack); if (err.status) { res.status(err.status).send(err.message); } else { res.status(500).send('Something broke!'); } };
-
Throw an error in one of your routes to test the custom error handler:
// In routes/userRoutes.js router.get('/error', (req, res, next) => { const err = new Error('Something went wrong!'); err.status = 500; next(err); });
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000/user/error
. You should see the custom error message:Something went wrong!
Route Handlers
Route handlers are functions that are executed when a route is matched. You can define multiple handlers for a route.
-
Modify
userRoutes.js
to include multiple handlers for a route:const express = require('express'); const router = express.Router(); // Middleware to parse JSON data router.use(express.json()); // Route to handle GET requests to /user with multiple handlers router.get('/', (req, res, next) => { console.log('First handler'); next(); }, (req, res) => { console.log('Second handler'); res.send('User List'); }); // Route to handle GET requests to /user/:id router.get('/:id', (req, res) => { res.send(`User ID: ${req.params.id}`); }); // Route to handle POST requests to /user router.post('/', (req, res) => { res.send('User created successfully'); }); // Route to handle PUT requests to /user/:id router.put('/:id', (req, res) => { res.send(`User ID ${req.params.id} updated successfully`); }); // Route to handle DELETE requests to /user/:id router.delete('/:id', (req, res) => { res.send(`User ID ${req.params.id} deleted successfully`); }); // Route to handle errors router.get('/error', (req, res, next) => { const err = new Error('Something went wrong!'); err.status = 500; next(err); }); module.exports = router;
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000/user
. You should see the message:User List
And see the logs in the terminal:
First handler Second handler
Connecting to Databases
Connecting to a database is essential for most web applications. Express.js can connect to a variety of databases like MongoDB, MySQL, and PostgreSQL.
Overview of Database Integration
Express.js provides libraries like Mongoose, Sequelize, and Knex.js to integrate with different databases. We'll focus on MongoDB with Mongoose.
Examples of Popular Databases
- MongoDB: A NoSQL database that uses JSON-like documents.
- MySQL: A relational database management system.
- PostgreSQL: Another relational database management system.
MongoDB with Mongoose
Mongoose is an ODM (Object Data Modeling) library for MongoDB and Node.js, providing a straightforward, schema-based solution to model your application data.
Setting Up Mongoose
-
Install Mongoose:
npm install mongoose
-
Connect to MongoDB in
app.js
:const express = require('express'); const exphbs = require('express-handlebars'); const userRoutes = require('./routes/userRoutes'); const mongoose = require('mongoose'); const errorHandler = require('./errors'); const app = express(); const PORT = 3000; // Connect to MongoDB mongoose.connect('mongodb://localhost:27017/mydatabase', { useNewUrlParser: true, useUnifiedTopology: true, }); // Set the view engine to Handlebars app.engine('handlebars', exphbs()); app.set('view engine', 'handlebars'); // Middleware to parse JSON data app.use(express.json()); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.render('index'); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Use user routes app.use('/user', userRoutes); // Custom error handler app.use(errorHandler); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
Models and Schemas
Mongoose models define the structure of the data and are used to interact with the database.
-
Create a new file named
models/User.js
and define a User model:const mongoose = require('mongoose'); const userSchema = new mongoose.Schema({ name: String, email: String, }); const User = mongoose.model('User', userSchema); module.exports = User;
-
Modify
routes/userRoutes.js
to use the User model:const express = require('express'); const User = require('../models/User'); const router = express.Router(); // Middleware to parse JSON data router.use(express.json()); // Route to handle GET requests to /user router.get('/', async (req, res, next) => { try { const users = await User.find(); res.json(users); } catch (err) { next(err); } }); // Route to handle GET requests to /user/:id router.get('/:id', async (req, res, next) => { try { const user = await User.findById(req.params.id); if (!user) { const err = new Error('User not found'); err.status = 404; return next(err); } res.json(user); } catch (err) { next(err); } }); // Route to handle POST requests to /user router.post('/', async (req, res, next) => { try { const newUser = new User(req.body); await newUser.save(); res.status(201).json(newUser); } catch (err) { next(err); } }); // Route to handle PUT requests to /user/:id router.put('/:id', async (req, res, next) => { try { const updatedUser = await User.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true }); if (!updatedUser) { const err = new Error('User not found'); err.status = 404; return next(err); } res.json(updatedUser); } catch (err) { next(err); } }); // Route to handle DELETE requests to /user/:id router.delete('/:id', async (req, res, next) => { try { const deletedUser = await User.findByIdAndDelete(req.params.id); if (!deletedUser) { const err = new Error('User not found'); err.status = 404; return next(err); } res.send(`User ID ${req.params.id} deleted successfully`); } catch (err) { next(err); } }); // Route to handle errors router.get('/error', (req, res, next) => { const err = new Error('Something went wrong!'); err.status = 500; next(err); }); module.exports = router;
-
Run the server:
node app.js
-
Use Postman or curl to test CRUD operations for the user routes.
CRUD Operations
CRUD stands for Create, Read, Update, and Delete, the four basic functions performed on data.
-
Creating a user:
POST http://localhost:3000/user Content-Type: application/json { "name": "Jane Doe", "email": "janedoe@example.com" }
Response:
{ "_id": "651d3e0b1e5f2c14aebf7a4a", "name": "Jane Doe", "email": "janedoe@example.com", "__v": 0 }
-
Reading a user:
GET http://localhost:3000/user/651d3e0b1e5f2c14aebf7a4a
Response:
{ "_id": "651d3e0b1e5f2c14aebf7a4a", "name": "Jane Doe", "email": "janedoe@example.com", "__v": 0 }
-
Updating a user:
PUT http://localhost:3000/user/651d3e0b1e5f2c14aebf7a4a Content-Type: application/json { "name": "Jane Smith" }
Response:
{ "_id": "651d3e0b1e5f2c14aebf7a4a", "name": "Jane Smith", "email": "janedoe@example.com", "__v": 1 }
-
Deleting a user:
DELETE http://localhost:3000/user/651d3e0b1e5f2c14aebf7a4a
Response:
User ID 651d3e0b1e5f2c14aebf7a4a deleted successfully
Static Files and Middleware
Serving static files, such as images, CSS, and JavaScript, is a common requirement in web applications.
Serving Static Files
You can serve static files using the express.static
middleware.
Basic Usage
-
Create a new directory named
public
in your project directory. -
Inside the
public
directory, create a file namedstyle.css
:body { font-family: Arial, sans-serif; background-color: #f0f0f0; } h1 { color: #333; }
-
Modify
app.js
to serve static files from thepublic
directory:const express = require('express'); const exphbs = require('express-handlebars'); const userRoutes = require('./routes/userRoutes'); const mongoose = require('mongoose'); const errorHandler = require('./errors'); const app = express(); const PORT = 3000; // Connect to MongoDB mongoose.connect('mongodb://localhost:27017/mydatabase', { useNewUrlParser: true, useUnifiedTopology: true, }); // Set the view engine to Handlebars app.engine('handlebars', exphbs()); app.set('view engine', 'handlebars'); // Middleware to serve static files app.use(express.static('public')); // Middleware to parse JSON data app.use(express.json()); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.render('index', { title: 'Home' }); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Use user routes app.use('/user', userRoutes); // Custom error handler app.use(errorHandler); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Modify
views/index.handlebars
to include the CSS file:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{title}}</title> <link rel="stylesheet" href="/style.css"> </head> <body> <h1>{{title}}</h1> </body> </html>
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000
. The page should have the styles fromstyle.css
.
Middleware Usage
You can use middleware to serve static files conditionally or customize their behavior.
-
Modify
app.js
to serve static files only in production:const express = require('express'); const exphbs = require('express-handlebars'); const userRoutes = require('./routes/userRoutes'); const mongoose = require('mongoose'); const errorHandler = require('./errors'); const app = express(); const PORT = 3000; // Connect to MongoDB mongoose.connect('mongodb://localhost:27017/mydatabase', { useNewUrlParser: true, useUnifiedTopology: true, }); // Set the view engine to Handlebars app.engine('handlebars', exphbs()); app.set('view engine', 'handlebars'); // Middleware to serve static files in production if (process.env.NODE_ENV === 'production') { app.use(express.static('public')); } // Middleware to parse JSON data app.use(express.json()); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.render('index', { title: 'Home' }); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Use user routes app.use('/user', userRoutes); // Custom error handler app.use(errorHandler); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Set the
NODE_ENV
environment variable toproduction
:NODE_ENV=production node app.js
-
Run the server:
node app.js
-
Open your web browser and navigate to
http://localhost:3000
. The page should have the styles fromstyle.css
.
Security Considerations
Serving static files from the wrong directory can expose sensitive information. Always ensure you serve static files from a secure location.
Security Best Practices
Protecting your Express.js application from security vulnerabilities is critical.
Basic Security Principles
- Keep your dependencies up to date using
npm update
andnpm audit
. - Use HTTPS to encrypt data in transit.
- Implement rate limiting to prevent abuse.
- Use secure HTTP headers to protect against common attacks.
- Validate and sanitize all user inputs to prevent injection attacks.
Protecting Against Common Vulnerabilities
-
Use middleware like
helmet
to set HTTP headers for security:npm install helmet
const express = require('express'); const exphbs = require('express-handlebars'); const userRoutes = require('./routes/userRoutes'); const mongoose = require('mongoose'); const errorHandler = require('./errors'); const helmet = require('helmet'); const app = express(); const PORT = 3000; // Connect to MongoDB mongoose.connect('mongodb://localhost:27017/mydatabase', { useNewUrlParser: true, useUnifiedTopology: true, }); // Use Helmet middleware to set HTTP headers app.use(helmet()); // Set the view engine to Handlebars app.engine('handlebars', exphbs()); app.set('view engine', 'handlebars'); // Middleware to serve static files in production if (process.env.NODE_ENV === 'production') { app.use(express.static('public')); } // Middleware to parse JSON data app.use(express.json()); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.render('index', { title: 'Home' }); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Use user routes app.use('/user', userRoutes); // Custom error handler app.use(errorHandler); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.js
NPM Security Tools
Use npm tools to identify and fix security vulnerabilities:
-
Run security audit:
npm audit
-
Fix security vulnerabilities:
npm audit fix
Debugging Express.js Applications
Debugging is a crucial part of developing applications. Let’s explore some basic debugging techniques.
Basic Debugging Techniques
Using Node.js Debugger
Node.js has a built-in debugger that allows you to pause execution of your code, inspect variables, and step through your code.
-
Start the debugger:
node inspect app.js
-
Use debugger commands to pause, inspect, and step through your code.
Third-party Tools
There are several third-party tools such as Visual Studio Code, WebStorm, and Chrome DevTools that provide powerful debugging capabilities.
Deployment of Node.js and Express.js Applications
Deploying your application to a production environment is a crucial step in making it accessible to users.
Overview of Deployment
Deploying an Express.js application involves setting up a server, configuring environment variables, and deploying your code.
Platforms: Heroku, AWS, etc.
Popular platforms for deploying Node.js applications include Heroku, AWS, and DigitalOcean.
Deployment Steps
Basic Configuration
-
Create a
Procfile
in your project directory:web: node app.js
-
Create a
.env
file for environment variables:PORT=3000
-
Modify
app.js
to use the environment variable:const express = require('express'); const exphbs = require('express-handlebars'); const userRoutes = require('./routes/userRoutes'); const mongoose = require('mongoose'); const errorHandler = require('./errors'); const helmet = require('helmet'); require('dotenv').config(); const app = express(); const PORT = process.env.PORT || 3000; // Connect to MongoDB mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/mydatabase', { useNewUrlParser: true, useUnifiedTopology: true, }); // Use Helmet middleware to set HTTP headers app.use(helmet()); // Set the view engine to Handlebars app.engine('handlebars', exphbs()); app.set('view engine', 'handlebars'); // Middleware to serve static files in production if (process.env.NODE_ENV === 'production') { app.use(express.static('public')); } // Middleware to parse JSON data app.use(express.json()); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.render('index', { title: 'Home' }); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Use user routes app.use('/user', userRoutes); // Custom error handler app.use(errorHandler); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Add the
mongodb
dependency:npm install mongoose
-
Add the
dotenv
dependency:npm install dotenv
-
Create a
.env
file with your MongoDB URI:PORT=3000 MONGODB_URI=mongodb://localhost:27017/mydatabase
Environment Variables
Environment variables help you manage configuration settings across different environments (development, staging, production).
-
Modify
.env
to include a MongoDB URI for the production environment:PORT=3000 NODE_ENV=production MONGODB_URI=mongodb://localhost:27017/mydatabase MONGODB_URI_PROD=mongodb://production.example.com/mydatabase
-
Modify
app.js
to use the production MongoDB URI:const express = require('express'); const exphbs = require('express-handlebars'); const userRoutes = require('./routes/userRoutes'); const mongoose = require('mongoose'); const errorHandler = require('./errors'); const helmet = require('helmet'); require('dotenv').config(); const app = express(); const PORT = process.env.PORT || 3000; // Connect to MongoDB mongoose.connect(process.env.NODE_ENV === 'production' ? process.env.MONGODB_URI_PROD : process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true, }); // Use Helmet middleware to set HTTP headers app.use(helmet()); // Set the view engine to Handlebars app.engine('handlebars', exphbs()); app.set('view engine', 'handlebars'); // Middleware to serve static files in production if (process.env.NODE_ENV === 'production') { app.use(express.static('public')); } // Middleware to parse JSON data app.use(express.json()); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.render('index', { title: 'Home' }); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Use user routes app.use('/user', userRoutes); // Custom error handler app.use(errorHandler); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.js
Advanced Topics
As you become more comfortable with Node.js and Express.js, you can explore more advanced topics.
Environment Modules
ES modules provide a new standard for writing modular JavaScript code.
Introduction to ES Modules
ES modules are the official standard for writing modular JavaScript code and are supported by Node.js.
Usage in Express.js
-
Modify
package.json
to use ES modules:{ "name": "my-express-app", "version": "1.0.0", "main": "app.js", "scripts": { "start": "node app.js" }, "dependencies": { "express": "^4.18.2", "mongoose": "^6.10.0", "dotenv": "^16.0.3", "express-handlebars": "^6.0.10", "helmet": "^6.1.0" }, "type": "module" }
-
Update file extensions to
.mjs
and modify your files accordingly. For example, renameapp.js
toapp.mjs
and update themain
field inpackage.json
:{ "name": "my-express-app", "version": "1.0.0", "main": "app.mjs", "scripts": { "start": "node app.mjs" }, "dependencies": { "express": "^4.18.2", "mongoose": "^6.10.0", "dotenv": "^16.0.3", "express-handlebars": "^6.0.10", "helmet": "^6.1.0" }, "type": "module" }
-
Update
app.mjs
to use ES modules:import express from 'express'; import exphbs from 'express-handlebars'; import userRoutes from './routes/userRoutes.mjs'; import mongoose from 'mongoose'; import errorHandler from './errors.mjs'; import helmet from 'helmet'; import 'dotenv/config'; const app = express(); const PORT = process.env.PORT || 3000; // Connect to MongoDB mongoose.connect(process.env.NODE_ENV === 'production' ? process.env.MONGODB_URI_PROD : process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true, }); // Use Helmet middleware to set HTTP headers app.use(helmet()); // Set the view engine to Handlebars app.engine('handlebars', exphbs()); app.set('view engine', 'handlebars'); // Middleware to serve static files in production if (process.env.NODE_ENV === 'production') { app.use(express.static('public')); } // Middleware to parse JSON data app.use(express.json()); // Route to handle GET requests to the home page app.get('/', (req, res) => { res.render('index', { title: 'Home' }); }); // Route to handle GET requests to the about page app.get('/about', (req, res) => { res.send('About Page'); }); // Route to handle GET requests to the contact page app.get('/contact', (req, res) => { res.send('Contact Page'); }); // Use user routes app.use('/user', userRoutes); // Custom error handler app.use(errorHandler); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
-
Run the server:
node app.mjs
Using Async/Await in Express.js
Async/await makes asynchronous code more readable and manageable.
Example: Using Async/Await with Mongoose
-
Modify
routes/userRoutes.mjs
to use async/await with Mongoose:import express from 'express'; import User from '../models/User.mjs'; const router = express.Router(); // Middleware to parse JSON data router.use(express.json()); // Route to handle GET requests to /user router.get('/', async (req, res, next) => { try { const users = await User.find(); res.json(users); } catch (err) { next(err); } }); // Route to handle GET requests to /user/:id router.get('/:id', async (req, res, next) => { try { const user = await User.findById(req.params.id); if (!user) { const err = new Error('User not found'); err.status = 404; return next(err); } res.json(user); } catch (err) { next(err); } }); // Route to handle POST requests to /user router.post('/', async (req, res, next) => { try { const newUser = new User(req.body); await newUser.save(); res.status(201).json(newUser); } catch (err) { next(err); } }); // Route to handle PUT requests to /user/:id router.put('/:id', async (req, res, next) => { try { const updatedUser = await User.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true }); if (!updatedUser) { const err = new Error('User not found'); err.status = 404; return next(err); } res.json(updatedUser); } catch (err) { next(err); } }); // Route to handle DELETE requests to /user/:id router.delete('/:id', async (req, res, next) => { try { const deletedUser = await User.findByIdAndDelete(req.params.id); if (!deletedUser) { const err = new Error('User not found'); err.status = 404; return next(err); } res.send(`User ID ${req.params.id} deleted successfully`); } catch (err) { next(err); } }); // Route to handle errors router.get('/error', (req, res, next) => { const err = new Error('Something went wrong!'); err.status = 500; next(err); }); export default router;
-
Run the server:
node app.mjs
Testing Express.js Applications
Testing is essential to ensure the quality and reliability of your application.
Unit Tests
Unit tests verify small parts of your codebase independently.
-
Install Mocha and Chai for unit testing:
npm install mocha chai --save-dev
-
Create a new directory named
test
in your project directory. -
Create a new file named
test/userRoutes.test.js
and add the following code:import assert from 'assert'; import { describe, it } from 'mocha'; import request from 'supertest'; import app from '../app.mjs'; describe('User Routes', () => { it('should get a user by ID', done => { request(app) .get('/user/651d3e0b1e5f2c14aebf7a4a') .expect('Content-Type', /json/) .expect(200) .end((err, res) => { if (err) return done(err); assert.strictEqual(res.body.name, 'Jane Smith'); assert.strictEqual(res.body.email, 'janedoe@example.com'); done(); }); }); });
-
Add a test script to
package.json
:{ "name": "my-express-app", "version": "1.0.0", "main": "app.mjs", "scripts": { "start": "node app.mjs", "test": "mocha" }, "dependencies": { "express": "^4.18.2", "mongoose": "^6.10.0", "dotenv": "^16.0.3", "express-handlebars": "^6.0.10", "helmet": "^6.1.0" }, "devDependencies": { "mocha": "^10.2.0", "chai": "^4.3.7", "supertest": "^6.3.2" }, "type": "module" }
-
Run the tests:
npm test
Integration Tests
Integration tests verify the interaction between different parts of your application.
-
Modify
test/userRoutes.test.js
to include integration tests:import assert from 'assert'; import { describe, it } from 'mocha'; import request from 'supertest'; import app from '../app.mjs'; describe('User Routes', () => { it('should get a user by ID', done => { request(app) .get('/user/651d3e0b1e5f2c14aebf7a4a') .expect('Content-Type', /json/) .expect(200) .end((err, res) => { if (err) return done(err); assert.strictEqual(res.body.name, 'Jane Smith'); assert.strictEqual(res.body.email, 'janedoe@example.com'); done(); }); }); it('should create a new user', done => { request(app) .post('/user') .send({ name: 'John Doe', email: 'johndoe@example.com' }) .expect('Content-Type', /json/) .expect(201) .end((err, res) => { if (err) return done(err); assert.strictEqual(res.body.name, 'John Doe'); assert.strictEqual(res.body.email, 'johndoe@example.com'); done(); }); }); it('should update a user by ID', done => { request(app) .put('/user/651d3e0b1e5f2c14aebf7a4a') .send({ name: 'John Smith' }) .expect('Content-Type', /json/) .expect(200) .end((err, res) => { if (err) return done(err); assert.strictEqual(res.body.name, 'John Smith'); assert.strictEqual(res.body.email, 'janedoe@example.com'); done(); }); }); it('should delete a user by ID', done => { request(app) .delete('/user/651d3e0b1e5f2c14aebf7a4a') .expect('Content-Type', /text\/plain/) .expect(200) .end((err, res) => { if (err) return done(err); assert.strictEqual(res.text, 'User ID 651d3e0b1e5f2c14aebf7a4a deleted successfully'); done(); }); }); });
-
Run the tests:
npm test
With this comprehensive guide, you should have a solid foundation to build Node.js and Express.js applications. From setting up your environment to advanced topics, we covered a wide range of concepts. Feel free to explore more about these technologies to build more complex and robust applications. Happy coding!