Lesson 37 of 50 – Building a CRUD API with Express.js
74% Completed
74%

Building a CRUD API with Express.js

Learn how to create, read, update, and delete student records using Node.js, Express.js, HTTP methods, and JSON data.

In this lesson: You will build a complete student CRUD API, understand REST endpoints, validate input, return HTTP status codes, and test API requests using Postman.

1. What Is CRUD?

CRUD represents the four basic operations used to manage data in an application.

Operation Meaning HTTP Method
Create Add a new student POST
Read View student records GET
Update Modify student information PUT or PATCH
Delete Remove a student DELETE

2. Create the Project

Open your terminal and run these commands:

mkdir student-crud-api
cd student-crud-api
npm init -y
npm install express

Create a file named app.js.

Your project structure will be:

student-crud-api/
│
├── node_modules/
├── app.js
├── package.json
└── package-lock.json

3. Create the Express Application

First, import Express, create the application, and configure JSON request-body parsing.

app.js
const express = require('express');

const app = express();

app.use(express.json());

const PORT = 3000;

app.listen(PORT, () => {
    console.log(`Server running at http://localhost:${PORT}`);
});

Run the application:

node app.js

The server is now ready to receive requests on port 3000.

4. Create Sample Student Data

For this beginner project, we will store student records in a JavaScript array. Each student will have an ID, name, and course.

const students = [
    { id: 1, name: 'Amit', course: 'Node.js' },
    { id: 2, name: 'Rahul', course: 'JavaScript' },
    { id: 3, name: 'Priya', course: 'React' }
];
Important: This array is temporary storage. Records will reset whenever the server restarts. A database should be used for permanent storage in a real application.

5. READ – Get All Students

The GET method retrieves student records from the API.

GET /api/students
app.get('/api/students', (req, res) => {
    res.json(students);
});

Test URL:

GET http://localhost:3000/api/students

Example response:

[
    {
        "id": 1,
        "name": "Amit",
        "course": "Node.js"
    },
    {
        "id": 2,
        "name": "Rahul",
        "course": "JavaScript"
    },
    {
        "id": 3,
        "name": "Priya",
        "course": "React"
    }
]

6. READ – Get a Student by ID

Use a route parameter to find one student by their ID.

GET /api/students/:id
app.get('/api/students/:id', (req, res) => {
    const id = Number(req.params.id);

    if (!Number.isInteger(id) || id <= 0) {
        return res.status(400).json({
            message: 'Invalid student ID'
        });
    }

    const student = students.find(s => s.id === id);

    if (!student) {
        return res.status(404).json({
            message: 'Student not found'
        });
    }

    res.json(student);
});

Test URL:

GET http://localhost:3000/api/students/2

Response:

{
    "id": 2,
    "name": "Rahul",
    "course": "JavaScript"
}

7. CREATE – Add a New Student

The POST method creates a new student record. The client sends the name and course in the JSON request body.

POST /api/students
app.post('/api/students', (req, res) => {
    const { name, course } = req.body;

    if (
        typeof name !== 'string' ||
        !name.trim() ||
        typeof course !== 'string' ||
        !course.trim()
    ) {
        return res.status(400).json({
            message: 'Name and course are required'
        });
    }

    const newStudent = {
        id: students.length
            ? Math.max(...students.map(s => s.id)) + 1
            : 1,
        name: name.trim(),
        course: course.trim()
    };

    students.push(newStudent);

    res.status(201).json({
        message: 'Student created successfully',
        student: newStudent
    });
});

Request URL:

POST http://localhost:3000/api/students

Request body (JSON):

{
    "name": "Neha",
    "course": "Python"
}

Example response:

{
    "message": "Student created successfully",
    "student": {
        "id": 4,
        "name": "Neha",
        "course": "Python"
    }
}

The API returns HTTP status code 201 when the student is created successfully.

8. UPDATE – Replace Student Information

The PUT method replaces the editable information of an existing student. In this example, both the name and course are required.

PUT /api/students/:id
app.put('/api/students/:id', (req, res) => {
    const id = Number(req.params.id);
    const { name, course } = req.body;

    if (!Number.isInteger(id) || id <= 0) {
        return res.status(400).json({
            message: 'Invalid student ID'
        });
    }

    if (
        typeof name !== 'string' ||
        !name.trim() ||
        typeof course !== 'string' ||
        !course.trim()
    ) {
        return res.status(400).json({
            message: 'Name and course are required'
        });
    }

    const student = students.find(s => s.id === id);

    if (!student) {
        return res.status(404).json({
            message: 'Student not found'
        });
    }

    student.name = name.trim();
    student.course = course.trim();

    res.json({
        message: 'Student updated successfully',
        student: student
    });
});

Request URL:

PUT http://localhost:3000/api/students/2

Request body:

{
    "name": "Rahul Kumar",
    "course": "React Native"
}

9. UPDATE – Change Selected Fields Using PATCH

PATCH updates only the fields included in the request body.

PATCH /api/students/:id
app.patch('/api/students/:id', (req, res) => {
    const id = Number(req.params.id);

    if (!Number.isInteger(id) || id <= 0) {
        return res.status(400).json({
            message: 'Invalid student ID'
        });
    }

    const student = students.find(s => s.id === id);

    if (!student) {
        return res.status(404).json({
            message: 'Student not found'
        });
    }

    const { name, course } = req.body;

    if (name === undefined && course === undefined) {
        return res.status(400).json({
            message: 'Provide at least one field to update'
        });
    }

    if (name !== undefined) {
        if (typeof name !== 'string' || !name.trim()) {
            return res.status(400).json({
                message: 'Name must be a non-empty string'
            });
        }

        student.name = name.trim();
    }

    if (course !== undefined) {
        if (typeof course !== 'string' || !course.trim()) {
            return res.status(400).json({
                message: 'Course must be a non-empty string'
            });
        }

        student.course = course.trim();
    }

    res.json({
        message: 'Student updated successfully',
        student: student
    });
});

Example request:

PATCH http://localhost:3000/api/students/2
{
    "course": "Node.js Advanced"
}

Only the course changes. The student's name remains unchanged.

10. DELETE – Remove a Student

The DELETE method removes a student record from the array.

DELETE /api/students/:id
app.delete('/api/students/:id', (req, res) => {
    const id = Number(req.params.id);

    if (!Number.isInteger(id) || id <= 0) {
        return res.status(400).json({
            message: 'Invalid student ID'
        });
    }

    const index = students.findIndex(s => s.id === id);

    if (index === -1) {
        return res.status(404).json({
            message: 'Student not found'
        });
    }

    students.splice(index, 1);

    res.json({
        message: 'Student deleted successfully'
    });
});

Request URL:

DELETE http://localhost:3000/api/students/3

Example response:

{
    "message": "Student deleted successfully"
}

11. Complete CRUD API Code

The following is the complete working application. Copy this code into app.js instead of combining the separate examples manually.

Complete app.js
const express = require('express');

const app = express();
const PORT = 3000;

app.use(express.json());

let students = [
    { id: 1, name: 'Amit', course: 'Node.js' },
    { id: 2, name: 'Rahul', course: 'JavaScript' },
    { id: 3, name: 'Priya', course: 'React' }
];

let nextId = 4;

// GET: Get all students
app.get('/api/students', (req, res) => {
    res.json(students);
});

// GET: Get a student by ID
app.get('/api/students/:id', (req, res) => {
    const id = Number(req.params.id);

    if (!Number.isInteger(id) || id <= 0) {
        return res.status(400).json({
            message: 'Invalid student ID'
        });
    }

    const student = students.find(s => s.id === id);

    if (!student) {
        return res.status(404).json({
            message: 'Student not found'
        });
    }

    res.json(student);
});

// POST: Create a student
app.post('/api/students', (req, res) => {
    const { name, course } = req.body;

    if (
        typeof name !== 'string' ||
        !name.trim() ||
        typeof course !== 'string' ||
        !course.trim()
    ) {
        return res.status(400).json({
            message: 'Name and course are required'
        });
    }

    const newStudent = {
        id: nextId++,
        name: name.trim(),
        course: course.trim()
    };

    students.push(newStudent);

    res.status(201).json({
        message: 'Student created successfully',
        student: newStudent
    });
});

// PUT: Replace student details
app.put('/api/students/:id', (req, res) => {
    const id = Number(req.params.id);
    const { name, course } = req.body;

    if (!Number.isInteger(id) || id <= 0) {
        return res.status(400).json({
            message: 'Invalid student ID'
        });
    }

    if (
        typeof name !== 'string' ||
        !name.trim() ||
        typeof course !== 'string' ||
        !course.trim()
    ) {
        return res.status(400).json({
            message: 'Name and course are required'
        });
    }

    const student = students.find(s => s.id === id);

    if (!student) {
        return res.status(404).json({
            message: 'Student not found'
        });
    }

    student.name = name.trim();
    student.course = course.trim();

    res.json({
        message: 'Student updated successfully',
        student: student
    });
});

// PATCH: Update selected student fields
app.patch('/api/students/:id', (req, res) => {
    const id = Number(req.params.id);

    if (!Number.isInteger(id) || id <= 0) {
        return res.status(400).json({
            message: 'Invalid student ID'
        });
    }

    const student = students.find(s => s.id === id);

    if (!student) {
        return res.status(404).json({
            message: 'Student not found'
        });
    }

    const { name, course } = req.body;

    if (name === undefined && course === undefined) {
        return res.status(400).json({
            message: 'Provide at least one field to update'
        });
    }

    if (name !== undefined) {
        if (typeof name !== 'string' || !name.trim()) {
            return res.status(400).json({
                message: 'Name must be a non-empty string'
            });
        }

        student.name = name.trim();
    }

    if (course !== undefined) {
        if (typeof course !== 'string' || !course.trim()) {
            return res.status(400).json({
                message: 'Course must be a non-empty string'
            });
        }

        student.course = course.trim();
    }

    res.json({
        message: 'Student updated successfully',
        student: student
    });
});

// DELETE: Delete a student
app.delete('/api/students/:id', (req, res) => {
    const id = Number(req.params.id);

    if (!Number.isInteger(id) || id <= 0) {
        return res.status(400).json({
            message: 'Invalid student ID'
        });
    }

    const index = students.findIndex(s => s.id === id);

    if (index === -1) {
        return res.status(404).json({
            message: 'Student not found'
        });
    }

    students.splice(index, 1);

    res.json({
        message: 'Student deleted successfully'
    });
});

// Handle unknown routes
app.use((req, res) => {
    res.status(404).json({
        message: 'API endpoint not found'
    });
});

// Handle malformed JSON request bodies
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(err);

    res.status(500).json({
        message: 'Internal server error'
    });
});

app.listen(PORT, () => {
    console.log(`Server running at http://localhost:${PORT}`);
});
Storage note: This complete example uses an in-memory array and an incrementing ID. It is suitable for learning, but it does not preserve records after a restart and is not a substitute for database-backed storage in production.

12. Test the CRUD API with Postman

Start your server:

node app.js
Operation Method URL
Get all students GET /api/students
Get student ID 1 GET /api/students/1
Create student POST /api/students
Replace student PUT /api/students/1
Update selected fields PATCH /api/students/1
Delete student DELETE /api/students/1

For POST, PUT, and PATCH requests:

  1. Select the appropriate HTTP method in Postman.
  2. Enter the complete API URL.
  3. Select Body → raw → JSON.
  4. Enter the required JSON data.
  5. Click Send and inspect the response.

13. Practical Assignment

Assignment: Employee CRUD API

Build an API for managing employees.

  1. Create an employee array containing ID, name, and department.
  2. Create a GET endpoint to return all employees.
  3. Create a GET endpoint to return one employee by ID.
  4. Create a POST endpoint to add an employee.
  5. Create a PUT endpoint to replace employee details.
  6. Create a PATCH endpoint to update one field.
  7. Create a DELETE endpoint to remove an employee.
  8. Validate IDs and required fields.
  9. Return appropriate status codes and JSON error messages.
  10. Test all endpoints using Postman.
Lesson Summary: You learned CRUD operations, Express route handlers, input validation, HTTP methods, JSON responses, error handling, and how to test a complete student CRUD API.

Test Your Knowledge – MCQ Quiz

Select the correct answer for each question.

1. What does CRUD stand for?
2. Which HTTP method is commonly used to create a student?
3. Which HTTP status code commonly indicates successful resource creation?