Lesson 49 of 50 – Student Management API
98% Completed
98%

Student Management API Using Node.js

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.

Project Goal: Create a REST API that manages student records using CRUD operations and validates incoming request data.

1. What Is a Student Management API?

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:

  • Add a new student.
  • Display all students.
  • Display a student by ID.
  • Update student information.
  • Delete a student record.
  • Search students by name.
  • Filter students by course.
  • Validate student information.

2. Understand the API Endpoints

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

3. Create the Project

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.

Project Structure

student-api-project/
│
├── node_modules/
├── package.json
├── package-lock.json
└── server.js

4. Create the Express Server

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}`);
});

Run the Server

node server.js

Open the following URL:

http://localhost:3000/

Expected output:

Welcome to the Student Management API!

5. Create Sample Student Data

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;
This array is temporary storage. Records disappear when the server restarts. A database such as MySQL or MongoDB is needed for persistent student records.

6. Complete Student Management API Code

Replace the contents of server.js with the complete code below.

server.js – Complete Student API
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}`);
});

7. Test the Student API Using Postman

Start the server, open Postman, and test each endpoint. For requests with a body, select Body → raw → JSON.

A. Display All Students

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.

B. Add a Student

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"
    }
}

C. Display One Student

Method: GET

http://localhost:3000/api/students/1

D. Update Student Details

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.

E. Delete a Student

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"
    }
}

F. Search Students by Name

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.

G. Filter Students by Course

Method: GET

http://localhost:3000/api/courses/node.js/students

The course filter matches the complete course name, ignoring case.

8. Understand the HTTP Status Codes

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

9. How Does the API Work?

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.

10. Common Errors and Solutions

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.

11. Improvements for a Real Application

Before using this project for actual student records, consider adding the following features:

  • MySQL or MongoDB for persistent storage.
  • Database-level unique constraints for student email addresses.
  • Authentication and role-based authorization.
  • Pagination for large student lists.
  • More complete email validation.
  • Automated API tests.
  • Secure error logging and monitoring.
  • Database backups and access controls.
This is an educational API, not a production-ready student information system. It has no authentication or database, and its sample records are lost when the server restarts.

12. Practical Assignment

Assignment: Extend the Student API
  1. Add a phone field to each student.
  2. Add a city field.
  3. Create an endpoint to count students in each course.
  4. Add an endpoint that lists students in alphabetical order.
  5. Add a route to find a student by email.
  6. Test invalid input and duplicate email cases.
  7. Connect the API to MySQL or MongoDB as an advanced exercise.

13. Summary

  • Node.js and Express can build REST APIs for student records.
  • CRUD operations manage student information.
  • Route parameters identify individual student records.
  • Validation helps reject invalid request data.
  • Search and filter endpoints make records easier to find.
  • A database is necessary for persistent data storage.

Test Your Knowledge – Student Management API

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?