Introduction
Real-world Node.js development is more than writing small programs. Developers use Node.js to build APIs, backend services, authentication systems, file-processing applications, dashboards, and business applications. In this chapter, you will practice practical Node.js problems that are similar to tasks developers face in real projects. Each question starts simply and gradually introduces validation, APIs, files, databases, security, and project organization. Node.js Real-World practice questions with solutions help to understand the concepts.
Question 1: How do you create a simple user registration API?
Problem
Create a Node.js and Express.js API that accepts a user’s name, email, and age.
The API should:
- Accept JSON data
- Check required fields
- Return the created user
- Return an error when required data is missing
Solution
Install Express:
npm init -y
npm install express
Create app.js:
const express = require("express");
const app = express();
app.use(express.json());
app.post("/api/users", (req, res) => {
const {
name,
email,
age
} = req.body;
if (!name || !email || age === undefined) {
return res.status(400).json({
success: false,
message: "Name, email and age are required."
});
}
const user = {
name: name,
email: email,
age: Number(age)
};
res.status(201).json({
success: true,
message: "User registered successfully.",
user: user
});
});
app.listen(3000, () => {
console.log(
"Server running on port 3000"
);
});
Test Request
POST http://localhost:3000/api/users
JSON body:
{
"name": "Rahul",
"email": "rahul@example.com",
"age": 20
}
Response
{
"success": true,
"message": "User registered successfully.",
"user": {
"name": "Rahul",
"email": "rahul@example.com",
"age": 20
}
}
Step-by-Step Explanation
Read data from:
req.body
Validate it:
if (!name || !email || age === undefined) {
...
}
Create a user object:
const user = {
name: name,
email: email,
age: Number(age)
};
Return HTTP status 201 because a new resource was created.
Question 2: How do you prevent duplicate email registration?
Problem
Create a registration API that does not allow two users to register with the same email address.
Solution
For this beginner-friendly example, store users in an array.
const express =
require("express");
const app =
express();
app.use(
express.json()
);
const users = [];
app.post(
"/api/register",
(req, res) => {
const {
name,
email
} = req.body;
if (!name || !email) {
return res.status(400).json({
success: false,
message:
"Name and email are required."
});
}
const existingUser =
users.find(
user =>
user.email.toLowerCase() ===
email.toLowerCase()
);
if (existingUser) {
return res.status(409).json({
success: false,
message:
"Email is already registered."
});
}
const user = {
id:
users.length + 1,
name:
name,
email:
email.toLowerCase()
};
users.push(
user
);
res.status(201).json({
success: true,
message:
"Registration successful.",
user:
user
});
}
);
app.listen(
3000,
() => {
console.log(
"Server running on port 3000"
);
}
);
Test First Request
{
"name": "Rahul",
"email": "rahul@example.com"
}
The user is created.
Send the Same Email Again
{
"name": "Amit",
"email": "rahul@example.com"
}
Response:
{
"success": false,
"message": "Email is already registered."
}
Step-by-Step Explanation
Search for an existing user:
const existingUser =
users.find(
user =>
user.email.toLowerCase() ===
email.toLowerCase()
);
If found:
return res.status(409).json({
...
});
Question 3: How do you create a Todo API?
Problem
Build a simple Todo API that allows users to:
- Create a todo
- View all todos
- Mark a todo as completed
Solution
const express =
require("express");
const app =
express();
app.use(
express.json()
);
const todos = [];
app.post(
"/api/todos",
(req, res) => {
const {
task
} = req.body;
if (!task) {
return res.status(400).json({
success: false,
message:
"Task is required."
});
}
const todo = {
id:
todos.length + 1,
task:
task,
completed:
false
};
todos.push(
todo
);
res.status(201).json({
success: true,
todo:
todo
});
}
);
app.get(
"/api/todos",
(req, res) => {
res.json({
success: true,
todos:
todos
});
}
);
app.patch(
"/api/todos/:id",
(req, res) => {
const id =
Number(req.params.id);
const todo =
todos.find(
item =>
item.id === id
);
if (!todo) {
return res.status(404).json({
success: false,
message:
"Todo not found."
});
}
todo.completed =
true;
res.json({
success: true,
message:
"Todo completed.",
todo:
todo
});
}
);
app.listen(
3000,
() => {
console.log(
"Server running on port 3000"
);
}
);
Create a Todo
POST /api/todos
JSON:
{
"task": "Learn Node.js"
}
Response
{
"success": true,
"todo": {
"id": 1,
"task": "Learn Node.js",
"completed": false
}
}
Mark It Complete
PATCH /api/todos/1
Response
{
"success": true,
"message": "Todo completed.",
"todo": {
"id": 1,
"task": "Learn Node.js",
"completed": true
}
}
Important Point
Todo applications are excellent beginner projects because they teach CRUD operations, API routes, request bodies, route parameters, and state changes.
Question 4: How do you create a file upload API?
Problem
Create a Node.js API that accepts an uploaded image.
Solution
Install Multer:
npm install express multer
Create app.js:
const express =
require("express");
const multer =
require("multer");
const path =
require("path");
const app =
express();
const storage =
multer.diskStorage({
destination:
"uploads/",
filename:
(req, file, cb) => {
const extension =
path.extname(
file.originalname
);
const filename =
Date.now() +
extension;
cb(
null,
filename
);
}
});
const upload =
multer({
storage: storage
});
app.post(
"/api/upload",
upload.single("image"),
(req, res) => {
if (!req.file) {
return res.status(400).json({
success: false,
message:
"Image is required."
});
}
res.json({
success: true,
message:
"File uploaded successfully.",
filename:
req.file.filename
});
}
);
app.listen(
3000,
() => {
console.log(
"Server running on port 3000"
);
}
);
Step-by-Step Explanation
Multer handles multipart form-data uploads.
The important middleware is:
upload.single("image")
It expects one uploaded file with the field name:
image
The uploaded file is available through:
req.file
Important Point
For production applications, validate file type, file size, filename handling, storage location, and access permissions before accepting uploads.
Question 5: How do you create a protected admin route?
Problem
Create an API where only requests containing a specific API key can access the admin route.
Solution
const express =
require("express");
const app =
express();
const API_KEY =
"my-secret-key";
function checkApiKey(
req,
res,
next
) {
const key =
req.headers["x-api-key"];
if (key !== API_KEY) {
return res.status(401).json({
success: false,
message:
"Unauthorized access."
});
}
next();
}
app.get(
"/api/admin",
checkApiKey,
(req, res) => {
res.json({
success: true,
message:
"Welcome to the admin area."
});
}
);
app.listen(
3000,
() => {
console.log(
"Server running on port 3000"
);
}
);
Test Without API Key
GET /api/admin
Response:
{
"success": false,
"message": "Unauthorized access."
}
Test With API Key
Add this request header:
x-api-key: my-secret-key
The API will allow access.
Step-by-Step Explanation
Read the header:
req.headers["x-api-key"]
Check it:
if (key !== API_KEY) {
...
}
Allow the request:
next();
Important Point
This example teaches the middleware concept. Do not hard-code real secrets in source code. Production applications should use environment variables and a proper authentication system.
Question 6: How do you create an API that reads a JSON file?
Problem
Create a Node.js API that reads product information from a local JSON file.
Create products.json
[
{
"id": 1,
"name": "Laptop",
"price": 55000
},
{
"id": 2,
"name": "Keyboard",
"price": 1500
},
{
"id": 3,
"name": "Mouse",
"price": 800
}
]
Create app.js
const express =
require("express");
const fs =
require("fs/promises");
const app =
express();
app.get(
"/api/products",
async (req, res) => {
try {
const data =
await fs.readFile(
"products.json",
"utf8"
);
const products =
JSON.parse(data);
res.json({
success: true,
products:
products
});
} catch (error) {
console.error(
error
);
res.status(500).json({
success: false,
message:
"Unable to read products."
});
}
}
);
app.listen(
3000,
() => {
console.log(
"Server running on port 3000"
);
}
);
Test
GET http://localhost:3000/api/products
Step-by-Step Explanation
Read the file:
await fs.readFile(
"products.json",
"utf8"
);
Convert JSON text into JavaScript data:
JSON.parse(data);
Return the data:
res.json({
products: products
});
Important Point
A JSON file can be useful for small demonstrations, prototypes, and configuration-style data, but it is generally not a replacement for a database in a multi-user production application.
Question 7: How do you add search and pagination to a Node.js API?
Problem
Create an API that supports:
/api/products?search=laptop&page=1&limit=2
The API should search products and return only the requested page.
Solution
const express =
require("express");
const app =
express();
const products = [
{
id: 1,
name: "Laptop",
price: 55000
},
{
id: 2,
name: "Keyboard",
price: 1500
},
{
id: 3,
name: "Mouse",
price: 800
},
{
id: 4,
name: "Laptop Bag",
price: 2000
},
{
id: 5,
name: "Monitor",
price: 12000
}
];
app.get(
"/api/products",
(req, res) => {
const search =
req.query.search || "";
const page =
Number(req.query.page) || 1;
const limit =
Number(req.query.limit) || 2;
const filteredProducts =
products.filter(
product =>
product.name
.toLowerCase()
.includes(
search.toLowerCase()
)
);
const start =
(page - 1) * limit;
const end =
start + limit;
const results =
filteredProducts.slice(
start,
end
);
res.json({
success: true,
page:
page,
limit:
limit,
total:
filteredProducts.length,
products:
results
});
}
);
app.listen(
3000,
() => {
console.log(
"Server running on port 3000"
);
}
);
Test
GET http://localhost:3000/api/products?search=laptop&page=1&limit=2
Example Response
{
"success": true,
"page": 1,
"limit": 2,
"total": 2,
"products": [
{
"id": 1,
"name": "Laptop",
"price": 55000
},
{
"id": 4,
"name": "Laptop Bag",
"price": 2000
}
]
}
Step-by-Step Explanation
Read query parameters:
req.query.search
req.query.page
req.query.limit
Calculate the starting position:
const start =
(page - 1) * limit;
Get only the required records:
filteredProducts.slice(
start,
end
);
Important Point
Pagination becomes very important when an API contains thousands or millions of records. Production APIs should usually implement pagination at the database-query level rather than loading the entire dataset into memory.
Question 8: How do you create a centralized error handler?
Problem
Create an Express.js application where errors are handled by one central middleware instead of repeating error-response code everywhere.
Solution
const express =
require("express");
const app =
express();
app.use(
express.json()
);
app.get(
"/api/profile",
(req, res, next) => {
const error =
new Error(
"Profile could not be loaded."
);
next(error);
}
);
app.use(
(
error,
req,
res,
next
) => {
console.error(
error
);
res.status(500).json({
success: false,
message:
"Something went wrong."
});
}
);
app.listen(
3000,
() => {
console.log(
"Server running on port 3000"
);
}
);
Test
GET http://localhost:3000/api/profile
Response
{
"success": false,
"message": "Something went wrong."
}
Step-by-Step Explanation
Create an error:
const error =
new Error(
"Profile could not be loaded."
);
Pass it to the error middleware:
next(error);
The error handler has four parameters:
(
error,
req,
res,
next
)
Question 9: How do you create a health-check API for a real Node.js application?
Problem
Create an endpoint that tells you whether the Node.js application is running.
This type of endpoint is commonly useful for monitoring and deployment environments.
Solution
const express =
require("express");
const app =
express();
app.get(
"/health",
(req, res) => {
res.status(200).json({
status:
"ok",
message:
"Application is running.",
uptime:
process.uptime(),
timestamp:
new Date().toISOString()
});
}
);
app.listen(
3000,
() => {
console.log(
"Server running on port 3000"
);
}
);
Test
GET http://localhost:3000/health
Example Response
{
"status": "ok",
"message": "Application is running.",
"uptime": 24.532,
"timestamp": "2026-08-29T10:30:00.000Z"
}
Step-by-Step Explanation
process.uptime() tells you approximately how many seconds the Node.js process has been running.
process.uptime()
The timestamp is generated using:
new Date().toISOString()
Important Point
A health-check endpoint is useful for monitoring application availability. In a production system, a deeper readiness check may also verify dependencies such as the database.
Question 10: How do you build a small real-world backend project?
Problem
Build a Course Management API that allows an institute to manage courses.
The API should support:
GET /api/courses
GET /api/courses/:id
POST /api/courses
PUT /api/courses/:id
DELETE /api/courses/:id
Solution
Step 1: Install Express
npm init -y
npm install express
Step 2: Create app.js
const express =
require("express");
const app =
express();
app.use(
express.json()
);
let courses = [
{
id: 1,
name: "Python Programming",
duration: "3 Months",
fee: 15000
},
{
id: 2,
name: "Web Development",
duration: "6 Months",
fee: 25000
}
];
app.get(
"/api/courses",
(req, res) => {
res.json({
success: true,
count:
courses.length,
courses:
courses
});
}
);
app.get(
"/api/courses/:id",
(req, res) => {
const id =
Number(req.params.id);
const course =
courses.find(
item =>
item.id === id
);
if (!course) {
return res.status(404).json({
success: false,
message:
"Course not found."
});
}
res.json({
success: true,
course:
course
});
}
);
app.post(
"/api/courses",
(req, res) => {
const {
name,
duration,
fee
} = req.body;
if (
!name ||
!duration ||
fee === undefined
) {
return res.status(400).json({
success: false,
message:
"Name, duration and fee are required."
});
}
const newCourse = {
id:
courses.length > 0
? courses[courses.length - 1].id + 1
: 1,
name:
name.trim(),
duration:
duration.trim(),
fee:
Number(fee)
};
if (
Number.isNaN(
newCourse.fee
)
) {
return res.status(400).json({
success: false,
message:
"Fee must be a number."
});
}
courses.push(
newCourse
);
res.status(201).json({
success: true,
message:
"Course created successfully.",
course:
newCourse
});
}
);
app.put(
"/api/courses/:id",
(req, res) => {
const id =
Number(req.params.id);
const course =
courses.find(
item =>
item.id === id
);
if (!course) {
return res.status(404).json({
success: false,
message:
"Course not found."
});
}
const {
name,
duration,
fee
} = req.body;
if (
!name ||
!duration ||
fee === undefined
) {
return res.status(400).json({
success: false,
message:
"Name, duration and fee are required."
});
}
const numericFee =
Number(fee);
if (
Number.isNaN(
numericFee
)
) {
return res.status(400).json({
success: false,
message:
"Fee must be a number."
});
}
course.name =
name.trim();
course.duration =
duration.trim();
course.fee =
numericFee;
res.json({
success: true,
message:
"Course updated successfully.",
course:
course
});
}
);
app.delete(
"/api/courses/:id",
(req, res) => {
const id =
Number(req.params.id);
const index =
courses.findIndex(
item =>
item.id === id
);
if (
index === -1
) {
return res.status(404).json({
success: false,
message:
"Course not found."
});
}
const deletedCourse =
courses.splice(
index,
1
)[0];
res.json({
success: true,
message:
"Course deleted successfully.",
course:
deletedCourse
});
}
);
app.get(
"/health",
(req, res) => {
res.json({
status:
"ok",
message:
"Course API is running."
});
}
);
app.use(
(req, res) => {
res.status(404).json({
success: false,
message:
"API route not found."
});
}
);
app.listen(
3000,
() => {
console.log(
"Course API running on port 3000"
);
}
);
Step 3: Test All APIs
Get All Courses
GET /api/courses
Get One Course
GET /api/courses/1
Create Course
POST /api/courses
JSON:
{
"name": "Data Analytics",
"duration": "4 Months",
"fee": 20000
}
Update Course
PUT /api/courses/1
JSON:
{
"name": "Advanced Python",
"duration": "4 Months",
"fee": 18000
}
Delete Course
DELETE /api/courses/2
Health Check
GET /health
Project Flow
Client
↓
HTTP Request
↓
Express.js
↓
Route
↓
Validation
↓
Business Logic
↓
Data Storage
↓
JSON Response
What You Practiced
This project combines several real-world concepts:
- Express.js
- REST APIs
- Routing
- Route parameters
- Request bodies
- Validation
- CRUD operations
- HTTP status codes
- JSON responses
- Error responses
- Health checks
- 404 handling
Key Takeaways
1. Real-world Node.js means solving practical problems
Node.js is commonly used to build APIs, backend applications, automation tools, file-processing services, and web applications.
2. Express.js is useful for building APIs
Express provides routing, middleware, request handling, and response handling.
3. Always validate incoming data
Never assume that data from a client is correct.
4. Use appropriate HTTP status codes
Common status codes include:
200 OK
201 Created
400 Bad Request
401 Unauthorized
404 Not Found
409 Conflict
500 Internal Server Error
5. Use middleware for repeated logic
Authentication, logging, validation, and error handling can often be implemented through middleware.
6. Do not store important data only in arrays
Arrays are useful for learning but are not suitable as the primary storage mechanism for most production applications.
7. Databases provide persistent storage
Use MongoDB, PostgreSQL, MySQL, or another suitable database for production data.
8. Keep secrets outside your source code
API keys, database passwords, JWT secrets, and other sensitive configuration should normally be stored using environment variables or a dedicated secrets-management system.
9. Centralized error handling improves API quality
A common error handler provides consistent responses and makes debugging easier.
10. File uploads require security checks
Check file size, type, storage location, and access rules before accepting uploaded files.
11. Search and pagination improve large APIs
Query parameters can be used to search, filter, sort, and paginate resources.
12. Health checks help monitor applications
A /health endpoint can quickly indicate whether an application process is responding.
13. Use meaningful API URLs
Prefer resource-oriented endpoints such as:
/api/users
/api/courses
/api/products
instead of unclear URLs.
14. Separate application responsibilities
As an application grows, separate:
Routes
Controllers
Services
Models
Middleware
Configuration
15. Authentication and authorization are different
Authentication answers:
Who are you?
Authorization answers:
What are you allowed to do?
16. Test APIs before connecting a frontend
Tools such as Postman, Insomnia, or similar API clients can help you test endpoints independently.
17. REST APIs should return predictable responses
A consistent JSON response structure makes APIs easier for frontend and mobile developers to consume.
18. Logging is important in production
Useful logs can help developers understand errors, requests, and application behavior.
19. Environment-specific configuration is important
Development, testing, and production environments may require different database URLs, ports, API keys, and other configuration values.
20. Build projects instead of only reading theory
Projects such as Todo APIs, Course APIs, Student APIs, Product APIs, and Expense APIs help turn Node.js concepts into practical development skills.
FAQs
1. What are real-world Node.js projects?
Real-world Node.js projects are applications that solve practical problems using Node.js. Examples include REST APIs, authentication systems, course management systems, ecommerce backends, file-upload services, dashboards, and database-driven applications.
2. Is Node.js suitable for real-world applications?
Yes. Node.js is widely used for backend services, APIs, web applications, real-time applications, automation, and other server-side workloads.
3. What should I build to practice Node.js?
Beginners can start with:
- Todo API
- Student Management API
- Course Management API
- Product API
- Blog API
- Expense Tracker API
After that, add authentication, databases, validation, file uploads, and role-based access.
4. Should I use MongoDB or MySQL with Node.js?
Both can work well. MongoDB is a document database, while MySQL is a relational database. The better choice depends on your application’s data structure, relationships, queries, team experience, and project requirements.
5. How do I make a Node.js project production-ready?
A production application usually needs more than basic API routes. Important areas include validation, authentication, authorization, database design, secure configuration, error handling, logging, testing, monitoring, rate limiting, and secure deployment.
6. Why should Node.js projects use environment variables?
Environment variables allow configuration and sensitive values to stay outside the source code.
For example:
PORT=3000
DATABASE_URL=your-database-url
JWT_SECRET=your-secret
They help keep configuration different between development, testing, and production.
7. What is the best way to learn Node.js through projects?
Learn one concept and immediately use it in a small project. Start with a basic API, then add CRUD operations, validation, database integration, authentication, error handling, file uploads, testing, and deployment. This gradually takes you from beginner-level Node.js to practical backend development.
Written by Shubhranshu Shekhar, who has trained 20000+ students in coding.
