Introduction
Error handling is an essential part of Node.js development. Applications can face errors because of invalid input, missing files, incorrect code, failed operations, or unexpected situations. In this chapter, you will practice handling errors using try...catch, throw, error objects, callbacks, Promises, async/await, HTTP responses, and Express error-handling middleware. Each example starts simple and gradually moves toward real-world Node.js applications. Node.js Error handling practice questions with solutions help to understand the concepts.
Question 1: How do you handle an error using try…catch?
Problem
Create a Node.js program that handles an error without crashing the application.
Solution
Create index.js:
try {
const result = 10 / 0;
console.log("Result:", result);
throw new Error("Something went wrong!");
} catch (error) {
console.log("Error:", error.message);
}
Output
Result: Infinity
Error: Something went wrong!
Step-by-Step Explanation
The try block contains code that may produce an error:
try {
// Code that may cause an error
}
The catch block handles the error:
catch (error) {
console.log(error.message);
}
The error variable contains information about the error.
Question 2: How do you handle an error caused by invalid JSON?
Problem
Try to convert invalid JSON into a JavaScript object and handle the error.
Solution
const jsonData = '{"name": "Rahul", age: 20}';
try {
const student = JSON.parse(jsonData);
console.log(student);
} catch (error) {
console.log("Invalid JSON!");
console.log("Error:", error.message);
}
Output
Invalid JSON!
Error: Expected property name or '}' in JSON...
The exact error message can vary by Node.js version.
Step-by-Step Explanation
JSON.parse() converts JSON text into a JavaScript value.
Valid JSON:
{
"name": "Rahul",
"age": 20
}
Invalid JSON:
{
"name": "Rahul",
age: 20
}
The age property needs double quotes.
Because invalid JSON causes JSON.parse() to throw an error, we use:
try {
// JSON parsing
} catch (error) {
// Handle error
}
Correct JSON
const jsonData = '{"name": "Rahul", "age": 20}';
const student = JSON.parse(jsonData);
console.log(student);
Question 3: How do you create your own error using throw?
Problem
Create a function that accepts a person’s age and throws an error if the age is below 18.
Solution
function checkAge(age) {
if (age < 18) {
throw new Error("Age must be 18 or above.");
}
return "You are eligible.";
}
try {
console.log(checkAge(15));
} catch (error) {
console.log("Error:", error.message);
}
Output
Error: Age must be 18 or above.
Step-by-Step Explanation
The throw statement creates an error:
throw new Error("Age must be 18 or above.");
When the condition is true, the function stops and throws the error.
The catch block receives it:
catch (error) {
console.log(error.message);
}
Try Another Value
Change:
checkAge(15)
to:
checkAge(20)
Output:
You are eligible.
Question 4: How do you handle file system errors?
Problem
Try to read a file that does not exist and handle the error.
Solution
const fs = require("fs");
fs.readFile("missing.txt", "utf8", (error, data) => {
if (error) {
console.log("Unable to read the file.");
console.log("Error:", error.message);
return;
}
console.log(data);
});
Output
Unable to read the file.
Error: ENOENT: no such file or directory...
The exact message can vary by operating system and Node.js version.
Step-by-Step Explanation
Node.js uses the callback pattern for many asynchronous file system operations.
The callback receives:
(error, data)
First, check the error:
if (error) {
console.log(error.message);
return;
}
If there is no error, use the file data:
console.log(data);
Question 5: How do you handle errors with Promises?
Problem
Create a Promise that rejects and handle the error using .catch().
Solution
const getUser = new Promise((resolve, reject) => {
const userFound = false;
if (userFound) {
resolve("User found.");
} else {
reject(new Error("User not found."));
}
});
getUser
.then((message) => {
console.log(message);
})
.catch((error) => {
console.log("Error:", error.message);
});
Output
Error: User not found.
Step-by-Step Explanation
A Promise can:
- Resolve successfully.
- Reject because something went wrong.
Success:
resolve("User found.");
Error:
reject(new Error("User not found."));
The .catch() method handles the rejected Promise:
.catch((error) => {
console.log(error.message);
});
Question 6: How do you handle errors using async/await?
Problem
Create an asynchronous function that handles a rejected Promise using try...catch.
Solution
function getUser() {
return Promise.reject(
new Error("User data could not be loaded.")
);
}
async function showUser() {
try {
const user = await getUser();
console.log(user);
} catch (error) {
console.log("Error:", error.message);
}
}
showUser();
Output
Error: User data could not be loaded.
Step-by-Step Explanation
The function returns a rejected Promise:
return Promise.reject(
new Error("User data could not be loaded.")
);
Because getUser() is asynchronous, we use:
await getUser();
The error is handled using:
try {
// await operation
} catch (error) {
// handle error
}
Question 7: How do you create a custom Error class?
Problem
Create a custom error called ValidationError for invalid user input.
Solution
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
function validateUsername(username) {
if (username.length < 3) {
throw new ValidationError(
"Username must contain at least 3 characters."
);
}
return "Username is valid.";
}
try {
console.log(validateUsername("AB"));
} catch (error) {
console.log("Error Type:", error.name);
console.log("Message:", error.message);
}
Output
Error Type: ValidationError
Message: Username must contain at least 3 characters.
Step-by-Step Explanation
We create a custom class:
class ValidationError extends Error
The class extends JavaScript’s built-in Error class.
Then:
this.name = "ValidationError";
gives the error a custom name.
Now we can throw it:
throw new ValidationError(
"Username must contain at least 3 characters."
);
Question 8: How do you send an error response from an HTTP server?
Problem
Create a Node.js HTTP server that returns a 404 response when a requested route does not exist.
Solution
const http = require("http");
const server = http.createServer((req, res) => {
if (req.url === "/") {
res.writeHead(200, {
"Content-Type": "text/plain"
});
res.end("Home Page");
} else {
res.writeHead(404, {
"Content-Type": "text/plain"
});
res.end("404 - Page Not Found");
}
});
server.listen(3000, () => {
console.log("Server running on port 3000");
});
Output
Start the server:
node index.js
Terminal:
Server running on port 3000
Open:
http://localhost:3000/
Output:
Home Page
Open:
http://localhost:3000/about
Output:
404 - Page Not Found
Step-by-Step Explanation
First, we check the URL:
if (req.url === "/")
If it is /, return status 200:
res.writeHead(200);
For an unknown route, return:
res.writeHead(404);
The 404 status tells the client that the requested resource was not found.
Question 9: How do you handle errors in Express using error-handling middleware?
Problem
Create an Express application that throws an error and handles it using error-handling middleware.
Solution
Install Express:
npm install express
Create index.js:
const express = require("express");
const app = express();
app.get("/", (req, res) => {
res.send("Home Page");
});
app.get("/error", (req, res) => {
throw new Error("Something went wrong!");
});
app.use((error, req, res, next) => {
console.error(error.message);
res.status(500).json({
error: "Internal Server Error"
});
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});
Run:
node index.js
Open:
http://localhost:3000/error
Output
{
"error": "Internal Server Error"
}
Step-by-Step Explanation
The route creates an error:
throw new Error("Something went wrong!");
The error-handling middleware is:
app.use((error, req, res, next) => {
// Handle error
});
Notice that it has four parameters:
(error, req, res, next)
This is how Express recognizes error-handling middleware.
Question 10: How do you build a complete error-handling system in Node.js?
Problem
Create an Express API that:
- Validates user input.
- Handles missing users.
- Uses custom errors.
- Returns proper HTTP status codes.
- Handles unexpected errors using middleware.
Solution
Install Express:
npm install express
Create index.js:
const express = require("express");
const app = express();
app.use(express.json());
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.name = "AppError";
}
}
const users = [
{
id: 1,
name: "Rahul"
},
{
id: 2,
name: "Priya"
}
];
app.get("/users/:id", (req, res, next) => {
try {
const id = Number(req.params.id);
if (Number.isNaN(id)) {
throw new AppError(
"User ID must be a number.",
400
);
}
const user = users.find(
(user) => user.id === id
);
if (!user) {
throw new AppError(
"User not found.",
404
);
}
res.json(user);
} catch (error) {
next(error);
}
});
app.use((error, req, res, next) => {
console.error(error);
const statusCode = error.statusCode || 500;
res.status(statusCode).json({
success: false,
error: error.message || "Internal Server Error"
});
});
app.listen(3000, () => {
console.log(
"Server running on http://localhost:3000"
);
});
Step-by-Step Explanation
Step 1: Create a custom error
class AppError extends Error
This allows us to store an HTTP status code along with the error message.
Step 2: Create sample users
const users = [
{
id: 1,
name: "Rahul"
},
{
id: 2,
name: "Priya"
}
];
Step 3: Create the API route
app.get("/users/:id", ...)
The :id represents a dynamic URL parameter.
Step 4: Convert the ID to a number
const id = Number(req.params.id);
Step 5: Validate the ID
if (Number.isNaN(id))
If the ID is not a number, throw a 400 error.
Step 6: Find the user
const user = users.find(
(user) => user.id === id
);
Step 7: Handle a missing user
if (!user) {
throw new AppError(
"User not found.",
404
);
}
Step 8: Pass errors to middleware
catch (error) {
next(error);
}
Step 9: Handle errors centrally
app.use((error, req, res, next) => {
// Error handling
});
HTTP status:
{
"success": false,
"error": "User ID must be a number."
}
HTTP status:
400
Key Takeaways
- Errors are a normal part of application development.
try...catchhandles synchronous JavaScript exceptions.throwallows you to create your own errors.new Error()creates a standard Error object.error.messagecontains the error message.error.nameidentifies the error type.- Node.js callback APIs commonly use the error-first callback pattern.
- Always check the
errorargument before using callback results. - Promise rejections can be handled using
.catch(). async/awaiterrors can commonly be handled withtry...catch.- Custom error classes can represent specific types of application errors.
- HTTP status codes communicate the result of a request to the client.
404commonly means a requested resource was not found.400commonly indicates an invalid client request.500commonly indicates an unexpected server-side error.- Express uses four-argument middleware for error handling:
(error, req, res, next)
- Centralized error-handling middleware keeps Express applications easier to maintain.
- Do not expose sensitive internal error details to users in production.
- Log useful diagnostic information on the server while returning safe messages to clients.
- Validate user input before processing it.
- Good error handling makes Node.js applications more reliable and easier to debug.
FAQs
1. What is error handling in Node.js?
Error handling is the process of detecting, managing, and responding to problems that occur while a Node.js application is running.
Common techniques include:
try...catch
throw
Promise.catch()
async/await with try...catch
callback error handling
Express error middleware
2. What is try…catch in Node.js?
try...catch allows you to handle exceptions without allowing the error to terminate the current synchronous operation unexpectedly.
Example:
try {
JSON.parse("invalid json");
} catch (error) {
console.log(error.message);
}
The try block contains risky code, while catch handles the error.
3. What is the purpose of throw in JavaScript?
throw allows you to create and send an error to the nearest appropriate error handler.
Example:
if (age < 18) {
throw new Error("Age must be 18 or above.");
}
The error can then be handled using try...catch.
4. How do you handle errors in a Node.js callback?
Node.js callback-based APIs commonly use the error-first pattern:
fs.readFile("file.txt", "utf8", (error, data) => {
if (error) {
console.log(error.message);
return;
}
console.log(data);
});
Always check the error argument before using the returned data.
5. How do you handle errors with async/await?
Use try...catch around the asynchronous operation:
async function getData() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.log(error.message);
}
}
This catches errors caused by a rejected Promise.
6. How does Express handle errors?
Express applications can use error-handling middleware.
Example:
app.use((error, req, res, next) => {
res.status(500).json({
error: "Something went wrong."
});
});
Express recognizes this middleware because it has four parameters:
(error, req, res, next)
7. What HTTP status code should be used for server errors?
A 500 status code is commonly used when the server encounters an unexpected condition.
For example:
res.status(500).json({
error: "Internal Server Error"
});
Other common status codes include:
400 → Bad Request
401 → Unauthorized
403 → Forbidden
404 → Not Found
500 → Internal Server Error
Written by Shubhranshu Shekhar, who has trained 20000+ students in coding.
