Build a complete Student Management REST API using Node.js and Express. Learn how to add students, view student records, update details, search students, and delete records.
A Student Management API allows an application to manage student information through HTTP requests. A frontend website or mobile application can communicate with this API to perform student-related operations.
Our project will include these features:
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/students |
Get all students |
| GET | /api/students/:id |
Get one student |
| POST | /api/students |
Add a student |
| PUT | /api/students/:id |
Update a student |
| DELETE | /api/students/:id |
Delete a student |
| GET | /api/students/search/:name |
Search by name |
| GET | /api/courses/:course/students |
Filter students by course |
Open your terminal and execute the following commands:
mkdir student-api-project
cd student-api-project
npm init -y
npm install express
Create a file named server.js.
student-api-project/
│
├── node_modules/
├── package.json
├── package-lock.json
└── server.js
First, import Express, create the application, and enable JSON request-body parsing.
const express = require('express');
const app = express();
app.use(express.json());
const PORT = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.send('Welcome to the Student Management API!');
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
node server.js
Open the following URL:
http://localhost:3000/
Expected output:
Welcome to the Student Management API!
For this learning project, we will use an array to store student records. Each student will have an ID, name, email, age, course, and admission date.
let students = [
{
id: 1,
name: 'Rahul Kumar',
email: 'rahul@example.com',
age: 20,
course: 'Node.js',
admissionDate: new Date().toISOString()
},
{
id: 2,
name: 'Priya Singh',
email: 'priya@example.com',
age: 19,
course: 'JavaScript',
admissionDate: new Date().toISOString()
}
];
let nextId = 3;
Replace the contents of server.js with the complete
code below.
const express = require('express');
const app = express();
app.use(express.json());
const PORT = process.env.PORT || 3000;
// Temporary student storage
let students = [
{
id: 1,
name: 'Rahul Kumar',
email: 'rahul@example.com',
age: 20,
course: 'Node.js',
admissionDate: new Date().toISOString()
},
{
id: 2,
name: 'Priya Singh',
email: 'priya@example.com',
age: 19,
course: 'JavaScript',
admissionDate: new Date().toISOString()
}
];
let nextId = 3;
// Validate student data
function validateStudent(data, isUpdate = false) {
const errors = [];
if (!data || typeof data !== 'object' || Array.isArray(data)) {
return ['Request body must be a JSON object'];
}
const allowedFields = ['name', 'email', 'age', 'course'];
const suppliedFields = Object.keys(data);
const invalidFields = suppliedFields.filter(
field => !allowedFields.includes(field)
);
if (invalidFields.length > 0) {
errors.push(
'Unsupported field(s): ' + invalidFields.join(', ')
);
}
if (!isUpdate || data.name !== undefined) {
if (
typeof data.name !== 'string' ||
!data.name.trim() ||
data.name.trim().length > 100
) {
errors.push('Name must be a non-empty string of at most 100 characters');
}
}
if (!isUpdate || data.email !== undefined) {
if (
typeof data.email !== 'string' ||
!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email.trim())
) {
errors.push('Enter a valid email address');
}
}
if (!isUpdate || data.age !== undefined) {
if (
!Number.isInteger(data.age) ||
data.age < 5 ||
data.age > 100
) {
errors.push('Age must be an integer between 5 and 100');
}
}
if (!isUpdate || data.course !== undefined) {
if (
typeof data.course !== 'string' ||
!data.course.trim() ||
data.course.trim().length > 100
) {
errors.push('Course must be a non-empty string of at most 100 characters');
}
}
if (isUpdate && suppliedFields.length === 0) {
errors.push('Provide at least one field to update');
}
return errors;
}
// Home route
app.get('/', (req, res) => {
res.send('Welcome to the Student Management API!');
});
// GET: Display all students
app.get('/api/students', (req, res) => {
res.json({
count: students.length,
students
});
});
// GET: Search students by name
app.get('/api/students/search/:name', (req, res) => {
const searchName = req.params.name.trim().toLowerCase();
if (!searchName) {
return res.status(400).json({
message: 'Search name is required'
});
}
const results = students.filter(student =>
student.name.toLowerCase().includes(searchName)
);
res.json({
count: results.length,
students: results
});
});
// GET: Filter students by course
app.get('/api/courses/:course/students', (req, res) => {
const course = req.params.course.trim().toLowerCase();
const results = students.filter(student =>
student.course.toLowerCase() === course
);
res.json({
count: results.length,
students: results
});
});
// GET: Display one student
app.get('/api/students/:id', (req, res) => {
const id = Number(req.params.id);
if (!Number.isInteger(id) || id < 1) {
return res.status(400).json({
message: 'Invalid student ID'
});
}
const student = students.find(student => student.id === id);
if (!student) {
return res.status(404).json({
message: 'Student not found'
});
}
res.json(student);
});
// POST: Add a new student
app.post('/api/students', (req, res) => {
const errors = validateStudent(req.body);
if (errors.length > 0) {
return res.status(400).json({
message: 'Validation failed',
errors
});
}
const email = req.body.email.trim().toLowerCase();
const emailExists = students.some(
student => student.email.toLowerCase() === email
);
if (emailExists) {
return res.status(409).json({
message: 'A student with this email already exists'
});
}
const student = {
id: nextId++,
name: req.body.name.trim(),
email,
age: req.body.age,
course: req.body.course.trim(),
admissionDate: new Date().toISOString()
};
students.push(student);
res.status(201).json({
message: 'Student added successfully',
student
});
});
// PUT: Update a student
app.put('/api/students/:id', (req, res) => {
const id = Number(req.params.id);
if (!Number.isInteger(id) || id < 1) {
return res.status(400).json({
message: 'Invalid student ID'
});
}
const student = students.find(student => student.id === id);
if (!student) {
return res.status(404).json({
message: 'Student not found'
});
}
const errors = validateStudent(req.body, true);
if (errors.length > 0) {
return res.status(400).json({
message: 'Validation failed',
errors
});
}
const newEmail = req.body.email === undefined
? student.email
: req.body.email.trim().toLowerCase();
const emailExists = students.some(
other =>
other.id !== id &&
other.email.toLowerCase() === newEmail
);
if (emailExists) {
return res.status(409).json({
message: 'A student with this email already exists'
});
}
if (req.body.name !== undefined) {
student.name = req.body.name.trim();
}
if (req.body.email !== undefined) {
student.email = newEmail;
}
if (req.body.age !== undefined) {
student.age = req.body.age;
}
if (req.body.course !== undefined) {
student.course = req.body.course.trim();
}
res.json({
message: 'Student updated successfully',
student
});
});
// DELETE: Remove a student
app.delete('/api/students/:id', (req, res) => {
const id = Number(req.params.id);
if (!Number.isInteger(id) || id < 1) {
return res.status(400).json({
message: 'Invalid student ID'
});
}
const studentIndex = students.findIndex(
student => student.id === id
);
if (studentIndex === -1) {
return res.status(404).json({
message: 'Student not found'
});
}
const deletedStudent = students.splice(studentIndex, 1)[0];
res.json({
message: 'Student deleted successfully',
student: deletedStudent
});
});
// Handle malformed JSON
app.use((err, req, res, next) => {
if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
return res.status(400).json({
message: 'Invalid JSON request body'
});
}
console.error('Unexpected server error');
res.status(500).json({
message: 'Internal server error'
});
});
// Handle unknown routes
app.use((req, res) => {
res.status(404).json({
message: 'Route not found'
});
});
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});
Start the server, open Postman, and test each endpoint. For requests with a body, select Body → raw → JSON.
Method: GET
http://localhost:3000/api/students
Example response:
{
"count": 2,
"students": [
{
"id": 1,
"name": "Rahul Kumar",
"email": "rahul@example.com",
"age": 20,
"course": "Node.js",
"admissionDate": "2026-01-01T10:00:00.000Z"
},
{
"id": 2,
"name": "Priya Singh",
"email": "priya@example.com",
"age": 19,
"course": "JavaScript",
"admissionDate": "2026-01-01T10:00:00.000Z"
}
]
}
The admission dates shown are illustrative. The application generates the actual dates when the server starts.
Method: POST
http://localhost:3000/api/students
JSON request body:
{
"name": "Amit Kumar",
"email": "amit@example.com",
"age": 21,
"course": "Python"
}
Example response:
{
"message": "Student added successfully",
"student": {
"id": 3,
"name": "Amit Kumar",
"email": "amit@example.com",
"age": 21,
"course": "Python",
"admissionDate": "2026-01-01T10:00:00.000Z"
}
}
Method: GET
http://localhost:3000/api/students/1
Method: PUT
http://localhost:3000/api/students/1
JSON request body:
{
"name": "Rahul Kumar",
"email": "rahul@example.com",
"age": 22,
"course": "Full Stack Development"
}
You may also send only the fields you want to update. The example accepts partial updates through PUT, although PATCH is commonly used for partial updates in REST APIs.
Method: DELETE
http://localhost:3000/api/students/2
Example response:
{
"message": "Student deleted successfully",
"student": {
"id": 2,
"name": "Priya Singh",
"email": "priya@example.com",
"age": 19,
"course": "JavaScript",
"admissionDate": "2026-01-01T10:00:00.000Z"
}
}
Method: GET
http://localhost:3000/api/students/search/rahul
This endpoint returns students whose names contain the search text, without distinguishing between uppercase and lowercase letters.
Method: GET
http://localhost:3000/api/courses/node.js/students
The course filter matches the complete course name, ignoring case.
| Status Code | Meaning |
|---|---|
| 200 | Request successful |
| 201 | Student created successfully |
| 400 | Invalid request data or student ID |
| 404 | Student or route not found |
| 409 | Email conflicts with an existing student |
| 500 | Unexpected server error |
Client (Postman or Frontend)
|
v
Express REST API
|
v
Route Handler
|
v
Validate Student Data
|
v
Read or Modify Student Array
|
v
Send JSON Response
Each request reaches an Express route. The application validates the request, performs the operation, and returns a response in JSON.
| Problem | Solution |
|---|---|
| Cannot find module 'express' | Run npm install express in the project directory. |
| Request body is undefined | Check that app.use(express.json()) appears before the routes. |
| Student not found | Check the student ID and verify that the record exists. |
| Email conflict | Use an email address that is not already assigned to another student. |
| Validation failed | Check that name, email, age, and course contain valid values. |
| Records disappear after restart | Use a database instead of temporary array storage. |
Before using this project for actual student records, consider adding the following features:
Choose the correct answer for each question.
1. Which HTTP method is used to add a new student?
2. What is the purpose of req.params.id?
3. Why should a real Student Management API use a database?