Introduction
REST APIs allow applications to communicate with each other using HTTP methods such as GET, POST, PUT, PATCH, and DELETE. In this chapter, you will practice building small Node.js REST API projects step by step. The examples start with simple APIs and gradually introduce route parameters, request bodies, validation, CRUD operations, search, filtering, status codes, and database-style API structures. Node.js REST API Projects Practice questions with solutions help to understand the concepts.
Question 1: How do you create a basic REST API with Node.js?
Problem
Create a simple REST API that returns a welcome message when a user visits /api.
Solution
First create a project:
mkdir rest-api-project
cd rest-api-project
npm init -y
npm install express
Create app.js:
const express = require("express");
const app = express();
app.get("/api", (req, res) => {
res.json({
success: true,
message: "Welcome to the Node.js REST API."
});
});
app.listen(3000, () => {
console.log(
"Server running on port 3000"
);
});
Run the Application
node app.js
Open:
http://localhost:3000/api
Response
{
"success": true,
"message": "Welcome to the Node.js REST API."
}
Step-by-Step Explanation
Create an Express application:
const app = express();
Create a GET endpoint:
app.get("/api", (req, res) => {
...
});
Send JSON:
res.json({
success: true,
message: "Welcome to the Node.js REST API."
});
Important Point
A REST API commonly sends data in JSON format so that different applications can easily consume the response.
Question 2: How do you create a REST API that returns multiple products?
Problem
Create an API:
GET /api/products
that returns a list of products.
Solution
Create app.js:
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
}
];
app.get(
"/api/products",
(req, res) => {
res.json({
success: true,
count:
products.length,
products:
products
});
}
);
app.listen(
3000,
() => {
console.log(
"Server running on port 3000"
);
}
);
Test
GET http://localhost:3000/api/products
Response
{
"success": true,
"count": 3,
"products": [
{
"id": 1,
"name": "Laptop",
"price": 55000
},
{
"id": 2,
"name": "Keyboard",
"price": 1500
},
{
"id": 3,
"name": "Mouse",
"price": 800
}
]
}
Step-by-Step Explanation
The products are stored in an array:
const products = [
...
];
The API returns the complete array:
res.json({
success: true,
count: products.length,
products: products
});
Important Point
This example uses an in-memory array for learning. Restarting the Node.js application will reset any changes made to the array.
Question 3: How do you create a REST API to get one product?
Problem
Create:
GET /api/products/:id
to return one product based on its ID.
Solution
app.get(
"/api/products/:id",
(req, res) => {
const id =
Number(req.params.id);
const product =
products.find(
item =>
item.id === id
);
if (!product) {
return res.status(404).json({
success: false,
message:
"Product not found."
});
}
res.json({
success: true,
product:
product
});
}
);
Test
Open:
http://localhost:3000/api/products/2
Response
{
"success": true,
"product": {
"id": 2,
"name": "Keyboard",
"price": 1500
}
}
Try an Invalid ID
http://localhost:3000/api/products/99
Response:
{
"success": false,
"message": "Product not found."
}
Step-by-Step Explanation
The :id is a route parameter:
req.params.id
Because URL parameters are strings, convert the ID:
const id =
Number(req.params.id);
Then search:
products.find(
item => item.id === id
);
Important Point
Use HTTP status 404 when the requested resource cannot be found.
Question 4: How do you create a POST REST API?
Problem
Create an API that allows users to add a new product.
Endpoint:
POST /api/products
Solution
First enable JSON request bodies:
app.use(
express.json()
);
Then create the POST route:
app.post(
"/api/products",
(req, res) => {
const {
name,
price
} = req.body;
if (
!name ||
price === undefined
) {
return res.status(400).json({
success: false,
message:
"Name and price are required."
});
}
const newProduct = {
id:
products.length + 1,
name:
name,
price:
price
};
products.push(
newProduct
);
res.status(201).json({
success: true,
message:
"Product created successfully.",
product:
newProduct
});
}
);
Test Request
POST http://localhost:3000/api/products
JSON body:
{
"name": "Monitor",
"price": 12000
}
Response
{
"success": true,
"message": "Product created successfully.",
"product": {
"id": 4,
"name": "Monitor",
"price": 12000
}
}
Step-by-Step Explanation
Read the request body:
const {
name,
price
} = req.body;
Validate required fields:
if (!name || price === undefined) {
...
}
Create a new product:
const newProduct = {
id: products.length + 1,
name: name,
price: price
};
Add it to the array:
products.push(
newProduct
);
Return status 201:
res.status(201).json(...);
Important Point
HTTP status 201 Created is commonly used when a REST API successfully creates a new resource.
Question 5: How do you create PUT and PATCH APIs?
Problem
Create APIs to update an existing product.
Use:
PUT /api/products/:id
PATCH /api/products/:id
Solution
PUT API
app.put(
"/api/products/:id",
(req, res) => {
const id =
Number(req.params.id);
const product =
products.find(
item =>
item.id === id
);
if (!product) {
return res.status(404).json({
success: false,
message:
"Product not found."
});
}
const {
name,
price
} = req.body;
if (
!name ||
price === undefined
) {
return res.status(400).json({
success: false,
message:
"Name and price are required."
});
}
product.name =
name;
product.price =
price;
res.json({
success: true,
message:
"Product updated successfully.",
product:
product
});
}
);
Test PUT
PUT http://localhost:3000/api/products/1
JSON:
{
"name": "Gaming Laptop",
"price": 75000
}
PATCH API
PATCH is useful when you want to update only selected fields.
app.patch(
"/api/products/:id",
(req, res) => {
const id =
Number(req.params.id);
const product =
products.find(
item =>
item.id === id
);
if (!product) {
return res.status(404).json({
success: false,
message:
"Product not found."
});
}
if (
req.body.name !== undefined
) {
product.name =
req.body.name;
}
if (
req.body.price !== undefined
) {
product.price =
req.body.price;
}
res.json({
success: true,
message:
"Product updated successfully.",
product:
product
});
}
);
Example PATCH Request
PATCH http://localhost:3000/api/products/1
JSON:
{
"price": 70000
}
Only the price changes.
Important Point
PUT is commonly used for replacing or fully updating a resource, while PATCH is designed for partial updates. Exact API semantics depend on how the API is designed.
Question 6: How do you create a DELETE REST API?
Problem
Create an API that deletes a product using:
DELETE /api/products/:id
Solution
app.delete(
"/api/products/:id",
(req, res) => {
const id =
Number(req.params.id);
const productIndex =
products.findIndex(
item =>
item.id === id
);
if (
productIndex === -1
) {
return res.status(404).json({
success: false,
message:
"Product not found."
});
}
const deletedProduct =
products.splice(
productIndex,
1
)[0];
res.json({
success: true,
message:
"Product deleted successfully.",
product:
deletedProduct
});
}
);
Test
DELETE http://localhost:3000/api/products/2
Response
{
"success": true,
"message": "Product deleted successfully.",
"product": {
"id": 2,
"name": "Keyboard",
"price": 1500
}
}
Step-by-Step Explanation
Find the product index:
const productIndex =
products.findIndex(
item => item.id === id
);
Delete one item:
products.splice(
productIndex,
1
);
Important Point
Always verify that the resource exists before deleting it.
Question 7: How do you add search functionality to a REST API?
Problem
Create an API that searches products by name.
Example:
GET /api/products/search?name=laptop
Solution
app.get(
"/api/products/search",
(req, res) => {
const search =
req.query.name;
if (!search) {
return res.status(400).json({
success: false,
message:
"Search name is required."
});
}
const results =
products.filter(
product =>
product.name
.toLowerCase()
.includes(
search.toLowerCase()
)
);
res.json({
success: true,
count:
results.length,
products:
results
});
}
);
Test
GET http://localhost:3000/api/products/search?name=laptop
Example Response
{
"success": true,
"count": 1,
"products": [
{
"id": 1,
"name": "Laptop",
"price": 55000
}
]
}
Step-by-Step Explanation
Read the query parameter:
req.query.name
For:
?name=laptop
the value is:
laptop
Search the array:
products.filter(
product =>
product.name
.toLowerCase()
.includes(
search.toLowerCase()
)
);
Important Point
Query parameters are useful for search, filtering, sorting, pagination, and other optional API controls.
Question 8: How do you add filtering to a REST API?
Problem
Create an API that returns products below a specified price.
Example:
GET /api/products?maxPrice=10000
Solution
app.get(
"/api/products",
(req, res) => {
const {
maxPrice
} = req.query;
let result =
products;
if (
maxPrice !== undefined
) {
const price =
Number(maxPrice);
if (
Number.isNaN(price)
) {
return res.status(400).json({
success: false,
message:
"maxPrice must be a number."
});
}
result =
products.filter(
product =>
product.price <= price
);
}
res.json({
success: true,
count:
result.length,
products:
result
});
}
);
Test
GET http://localhost:3000/api/products?maxPrice=10000
Example Response
{
"success": true,
"count": 2,
"products": [
{
"id": 2,
"name": "Keyboard",
"price": 1500
},
{
"id": 3,
"name": "Mouse",
"price": 800
}
]
}
Step-by-Step Explanation
Read the query:
req.query.maxPrice
Convert it into a number:
const price =
Number(maxPrice);
Filter products:
products.filter(
product =>
product.price <= price
);
Important Point
Query parameters are optional by nature. Your API should define what happens when they are missing or invalid.
Question 9: How do you build a REST API project with separate routes and controllers?
Problem
Organize the product REST API into separate files instead of keeping everything inside app.js.
Solution
Create:
rest-api/
│
├── controllers/
│ └── productController.js
│
├── routes/
│ └── productRoutes.js
│
├── app.js
└── package.json
Step 1: Create Controller
controllers/productController.js
const products = [
{
id: 1,
name: "Laptop",
price: 55000
},
{
id: 2,
name: "Keyboard",
price: 1500
}
];
function getProducts(
req,
res
) {
res.json({
success: true,
products:
products
});
}
function getProduct(
req,
res
) {
const id =
Number(req.params.id);
const product =
products.find(
item =>
item.id === id
);
if (!product) {
return res.status(404).json({
success: false,
message:
"Product not found."
});
}
res.json({
success: true,
product:
product
});
}
module.exports = {
getProducts,
getProduct
};
Step 2: Create Routes
routes/productRoutes.js
const express =
require("express");
const router =
express.Router();
const productController =
require("../controllers/productController");
router.get(
"/",
productController.getProducts
);
router.get(
"/:id",
productController.getProduct
);
module.exports =
router;
Step 3: Create app.js
const express =
require("express");
const productRoutes =
require("./routes/productRoutes");
const app =
express();
app.use(
express.json()
);
app.use(
"/api/products",
productRoutes
);
app.listen(
3000,
() => {
console.log(
"Server running on port 3000"
);
}
);
Test
Get all products:
GET http://localhost:3000/api/products
Get one product:
GET http://localhost:3000/api/products/1
Request Flow
Client
↓
/api/products
↓
productRoutes.js
↓
productController.js
↓
Response
Important Point
Separating routes and controllers becomes especially useful when a REST API contains many endpoints.
Question 10: How do you build a complete REST API project?
Problem
Build a small Student Management REST API with:
GET /api/students
GET /api/students/:id
POST /api/students
PUT /api/students/:id
DELETE /api/students/:id
Solution
Project Structure
student-api/
│
├── controllers/
│ └── studentController.js
│
├── routes/
│ └── studentRoutes.js
│
├── app.js
└── package.json
Step 1: Create the Controller
controllers/studentController.js
let students = [
{
id: 1,
name: "Rahul",
age: 20,
course: "Node.js"
},
{
id: 2,
name: "Priya",
age: 19,
course: "JavaScript"
}
];
function getStudents(
req,
res
) {
res.json({
success: true,
count:
students.length,
students:
students
});
}
function getStudent(
req,
res
) {
const id =
Number(req.params.id);
const student =
students.find(
item =>
item.id === id
);
if (!student) {
return res.status(404).json({
success: false,
message:
"Student not found."
});
}
res.json({
success: true,
student:
student
});
}
function createStudent(
req,
res
) {
const {
name,
age,
course
} = req.body;
if (
!name ||
age === undefined ||
!course
) {
return res.status(400).json({
success: false,
message:
"Name, age and course are required."
});
}
const newStudent = {
id:
students.length > 0
? students[students.length - 1].id + 1
: 1,
name:
name.trim(),
age:
Number(age),
course:
course.trim()
};
students.push(
newStudent
);
res.status(201).json({
success: true,
message:
"Student created successfully.",
student:
newStudent
});
}
function updateStudent(
req,
res
) {
const id =
Number(req.params.id);
const student =
students.find(
item =>
item.id === id
);
if (!student) {
return res.status(404).json({
success: false,
message:
"Student not found."
});
}
const {
name,
age,
course
} = req.body;
if (
!name ||
age === undefined ||
!course
) {
return res.status(400).json({
success: false,
message:
"Name, age and course are required."
});
}
student.name =
name.trim();
student.age =
Number(age);
student.course =
course.trim();
res.json({
success: true,
message:
"Student updated successfully.",
student:
student
});
}
function deleteStudent(
req,
res
) {
const id =
Number(req.params.id);
const studentIndex =
students.findIndex(
item =>
item.id === id
);
if (
studentIndex === -1
) {
return res.status(404).json({
success: false,
message:
"Student not found."
});
}
const deletedStudent =
students.splice(
studentIndex,
1
)[0];
res.json({
success: true,
message:
"Student deleted successfully.",
student:
deletedStudent
});
}
module.exports = {
getStudents,
getStudent,
createStudent,
updateStudent,
deleteStudent
};
Step 2: Create Routes
routes/studentRoutes.js
const express =
require("express");
const router =
express.Router();
const studentController =
require("../controllers/studentController");
router.get(
"/",
studentController.getStudents
);
router.get(
"/:id",
studentController.getStudent
);
router.post(
"/",
studentController.createStudent
);
router.put(
"/:id",
studentController.updateStudent
);
router.delete(
"/:id",
studentController.deleteStudent
);
module.exports =
router;
Step 3: Create the Main Application
app.js
const express =
require("express");
const studentRoutes =
require("./routes/studentRoutes");
const app =
express();
app.use(
express.json()
);
app.use(
"/api/students",
studentRoutes
);
app.get(
"/",
(req, res) => {
res.json({
success: true,
message:
"Student REST API is running."
});
}
);
app.listen(
3000,
() => {
console.log(
"Server running on port 3000"
);
}
);
Step 4: Test GET All Students
GET http://localhost:3000/api/students
Response
{
"success": true,
"count": 2,
"students": [
{
"id": 1,
"name": "Rahul",
"age": 20,
"course": "Node.js"
},
{
"id": 2,
"name": "Priya",
"age": 19,
"course": "JavaScript"
}
]
}
Step 5: Test GET One Student
GET http://localhost:3000/api/students/1
Step 6: Test POST
POST http://localhost:3000/api/students
JSON:
{
"name": "Amit",
"age": 21,
"course": "Express.js"
}
Step 7: Test PUT
PUT http://localhost:3000/api/students/1
JSON:
{
"name": "Rahul Kumar",
"age": 21,
"course": "Full Stack Development"
}
Step 8: Test DELETE
DELETE http://localhost:3000/api/students/2
Complete REST API Flow
Client
|
↓
HTTP Request
|
↓
Express
|
↓
Routes
|
↓
Controllers
|
↓
Data / Database
|
↓
Controllers
|
↓
JSON Response
|
↓
Client
CRUD Summary
| Operation | HTTP Method | Endpoint |
|---|---|---|
| Create | POST | /api/students |
| Read All | GET | /api/students |
| Read One | GET | /api/students/:id |
| Update | PUT | /api/students/:id |
| Delete | DELETE | /api/students/:id |
Important Point
This final project uses an array to keep the example beginner-friendly. For a real application, replace the array with a database such as MongoDB, PostgreSQL, or MySQL.
Key Takeaways
1. REST APIs allow applications to communicate
A frontend, mobile application, or another server can communicate with a Node.js REST API through HTTP requests.
2. GET is used for retrieving resources
Example:
GET /api/students
3. POST is used to create resources
Example:
POST /api/students
4. PUT is commonly used for full updates
Example:
PUT /api/students/1
5. PATCH is used for partial updates
Example:
PATCH /api/students/1
6. DELETE removes a resource
Example:
DELETE /api/students/1
7. Route parameters identify resources
For:
/api/students/10
you can access:
req.params.id
8. Query parameters provide optional controls
For:
/api/students?course=node
you can access:
req.query.course
9. Request bodies contain submitted data
For JSON requests:
req.body
can contain the submitted data.
10. Express JSON middleware is important
Use:
app.use(
express.json()
);
when your API needs to read JSON request bodies.
11. JSON is commonly used in REST APIs
Example:
{
"name": "Rahul",
"age": 20
}
12. Use meaningful HTTP status codes
Common examples include:
200 OK
201 Created
400 Bad Request
404 Not Found
500 Internal Server Error
13. Validate incoming data
Never assume that client-provided data is correct.
14. Separate routes and controllers
This makes larger REST API projects easier to organize.
15. REST APIs can use databases
A production REST API can connect to MongoDB, PostgreSQL, MySQL, or another database.
16. REST is not limited to websites
Mobile apps, desktop applications, frontend frameworks, and other backend services can consume REST APIs.
17. Search and filtering use query parameters
Examples:
/api/products?name=laptop
and:
/api/products?maxPrice=10000
18. Use authentication for protected resources
Private REST API endpoints should normally verify the identity and permissions of the requester.
19. Handle errors properly
A REST API should return useful status codes and predictable error responses.
20. Build small projects to master REST APIs
Good beginner projects include:
- Student Management API
- Product API
- Book Management API
- Todo API
- Employee API
- Course Management API
- Expense Tracker API
FAQs
1. What is a REST API in Node.js?
A REST API is a web API that allows applications to communicate through HTTP requests. In Node.js, Express.js is commonly used to create REST API endpoints.
For example:
GET /api/students
can return a list of students.
2. Which HTTP methods are commonly used in REST APIs?
The most common methods are:
GET
POST
PUT
PATCH
DELETE
GET retrieves data, POST creates data, PUT and PATCH update data, and DELETE removes data.
3. What is the difference between PUT and PATCH?
PUT is commonly used when replacing or fully updating a resource.
PATCH is designed for partial updates.
For example, PATCH could update only a student’s course without sending every other student field.
4. What is req.params in Express.js?
req.params contains route parameters.
For this URL:
/api/students/25
the route:
app.get(
"/api/students/:id",
(req, res) => {
console.log(
req.params.id
);
}
);
will produce:
25
5. What is req.query in Express.js?
req.query contains query-string parameters.
For:
/api/products?maxPrice=5000
you can access:
req.query.maxPrice
6. What is req.body in a REST API?
req.body contains data sent inside the request body.
For JSON requests, Express needs:
app.use(
express.json()
);
Then you can access:
req.body
7. How do I practice Node.js REST API development?
Start with small projects such as a Todo API or Student API. Practice creating GET, POST, PUT, PATCH, and DELETE endpoints. Then add validation, authentication, search, pagination, database integration, and error handling.
Written by Shubhranshu Shekhar, who has trained 20000+ students in coding.
