A Beginner's Guide to REST API

1. Introduction

This guide explains how to build a REST API using Node.js and Express. It is intended for beginners and uses a simple Student Management System as an example. Data will be stored in a local JSON file instead of a database to keep the implementation straightforward.

1.1 What is an API?

An API (Application Programming Interface) is a way for software applications to talk to each other. You can think of it like a waiter in a restaurant—you (the client) make a request, and the waiter (the API) brings back what you asked for from the kitchen (the server).

APIs help applications exchange data. For example:

  • A mobile app might use an API to fetch your weather forecast.
  • A web application might call an API to get user information from a database.

1.2 What is a REST API?

REST stands for Representational State Transfer. A REST API follows specific design rules that make it easy to interact with data using standard HTTP methods:

  • GET – to read data
  • POST – to create data
  • PUT – to update data
  • DELETE – to remove data

These APIs are easy to use and widely adopted on the web. In this guide, we will create a RESTful API to manage student records.

1.3 Project Overview

We will build a Student Management API with the following features:

  • Add new students (POST /users)
  • View all students (GET /users)
  • Display a welcome message on the root endpoint (GET /)

Instead of using a database, we will store student data in a local JSON file named db.json. This simplifies the setup and allows you to focus on understanding how the API works.

1.4 Technologies Used

Let’s briefly introduce the tools we’ll be using:

  • Node.js

Node.js lets you run JavaScript code outside the browser. It's commonly used for backend development. We'll use it to create our server.

  • Express.js

Express is a lightweight Node.js framework that simplifies the process of building APIs. It handles routing (defining what happens when someone visits a URL like /users) and more.

  • Postman

Postman is a graphical interface used to test APIs. You can send requests to your API and inspect the response without writing any code.

  • cURL

cURL is a command-line tool for sending HTTP requests. It's useful if you prefer working from the terminal.

  • File System (fs) module

Node.js provides the fs module to read from and write to files. We'll use this to store and retrieve student data from db.json.

  • Path module

The path module helps handle file paths in a way that works across different operating systems.

1.5 Introduction to Endpoints

An endpoint is a specific URL that the API listens to. Each endpoint performs a specific action and uses an HTTP method like GET or POST. Below are the endpoints we will create for the Student Management System:

MethodEndpointDescription
GET/Returns a basic welcome message
GET/studentsRetrieves a list of all students
POST/studentsAdds a new student to the system

These endpoints represent common operations in many REST APIs: reading data, writing data, and confirming the service is running.

Certainly. Here's the corrected and updated Section 2 with Step 5 (Open in VS Code) placed correctly after Step 2 (Create Project Folder), and the rest of the section adjusted accordingly for consistent flow:

2. Installation

This section guides you through setting up the environment required to build and run the Student Management API.

2.1 Prerequisites

Before getting started, ensure the following tools are installed on your system:

  • Node.js – JavaScript runtime environment Download and install from: https://nodejs.org

  • A code editor – Recommended: Visual Studio Code

  • Basic command-line knowledge – You will use a terminal to run commands for installing packages and starting the server.

2.2 Create the Project

This part walks you through creating the project directory, opening it in VS Code, initializing it with Node.js, and installing Express.

Step 1: Open a Terminal on Windows

You can use any of the following terminal applications:

  • Command Prompt – Search for cmd in the Start menu
  • PowerShell – Search for PowerShell in the Start menu
  • Git Bash – If installed, right-click in a folder and choose Git Bash Here

💡 All commands shown below will work the same way in any of these terminals.

Step 2: Create the Project Folder

Run the following commands to create a new project directory and navigate into it:

mkdir student-api
cd student-api

Step 3: Open the Project in Visual Studio Code

Once inside the folder, you can open the current project in VS Code by running:

code .

📝 If this command doesn’t work, you may need to enable the code command in PATH:

  • Open VS Code manually.
  • Go to View > Command Palette
  • Type and select: Shell Command: Install 'code' command in PATH
  • Restart your terminal and try again.

Step 4: Initialize a Node.js Project

Open the built-in terminal in Visual Studio Code:

  • From the top menu, go to: Terminal > New Terminal
  • Or use the keyboard shortcut: Ctrl + `

Then run the following command to initialize your project:

npm init -y

This creates a package.json file with default values, which is used to manage your project's configuration and dependencies.

Step 5: Install Express

Now install the Express.js framework by running:

npm install express

This command downloads and adds Express to your project under the node_modules folder and records it as a dependency in package.json.

2.3 Set Up Project Files

Next, manually create the following folder and files inside your project directory:

student-api/
├── db/
│   └── db.json
└── server.js

server.js

This file will contain the main Express application and your API routes.

db/db.json

This file will be used to store student records. For now, initialize it with an empty array:

[]

This represents an empty list of students.

2.4 Optional: Install Postman or cURL for API Testing

To test your API, you can use either Postman (a graphical tool) or cURL (a command-line tool).

Option 1: Postman (GUI-based Tool)

Postman allows you to manually send HTTP requests and view responses.

Option 2: cURL (Command-Line Tool)

On Windows 10 or later, cURL is included by default. To check:

curl --version

If you see version information, cURL is already available.

If cURL is not installed:

  1. Download the installer from: https://curl.se/windows/
  2. Choose the appropriate version (usually 64-bit with SSL support).
  3. Extract the ZIP archive.
  4. Add the folder containing curl.exe to your system’s PATH environment variable.
  5. Open a new terminal and verify with:
curl --version

3. Logical Workflow of the Implementation

This section explains how to build the REST API step by step. Each concept is accompanied by a code example and a detailed breakdown of its syntax, purpose, and structure.

3.1 Project Structure Overview

Your folder structure should look like this:

student-api/
├── db/
│   └── db.json        → JSON file used to store student data
└── server.js          → Main server file
  • db.json will act as a simple file-based database.
  • server.js is the main script where we define the server and all API endpoints.

3.2 Setting Up a Basic Express Server

Create a new file named server.js in the root folder and add the following code:

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

app.get('/', (req, res) => {
  res.send('Welcome to the Student Management API');
});

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

Explanation:

const express = require('express');
  • require('express'): Loads the Express module.
  • const express: We assign the imported module to a constant named express.
const app = express();
  • express(): Creates an instance of the Express application.
  • app: This variable is now used to define routes and middleware.
const PORT = 3000;
  • Defines the port number on which the server will listen.
app.get('/', (req, res) => { ... });
  • app.get: Defines a GET endpoint.

  • '/': The URL path (root of the API).

  • (req, res) => { ... }: Callback function that handles the request.

    • req: The incoming request object.
    • res: The response object used to send data back to the client.
res.send('Welcome to the Student Management API');
  • Sends a plain text response to the client.
app.listen(PORT, () => { ... });
  • Starts the server on the specified port.
  • The callback logs a message when the server is running.

How to Run the Server

  1. Open the integrated terminal by selecting Terminal > New Terminal from the top menu.

  2. In the terminal, run the following command to start the server:

    node server.js
  3. If the server starts successfully, you will see this message in the terminal:

    Server is running on http://localhost:3000
    
  4. Open your browser and visit http://localhost:3000. You should see the message:

    Welcome to the Student Management API
    

3.3 Enabling JSON Parsing with Middleware

Add this line before defining any endpoints that receive JSON data:

app.use(express.json());

Explanation:

  • app.use(...): Registers middleware in Express.
  • express.json(): Middleware that parses incoming JSON in the body of HTTP requests and makes it available as req.body.

This is necessary to handle POST requests that send JSON data.

3.4 Using File System and Path Modules

Add these lines at the top of server.js:

const fs = require('fs');
const path = require('path');

These are built-in Node.js modules.

Creating a Path to the JSON File

const dbPath = path.join(__dirname, 'db', 'db.json');

Explanation:

  • path.join(...): Joins path segments into a valid file path.
  • __dirname: Built-in variable in Node.js that gives the absolute path of the current directory (where server.js is located).
  • 'db': The folder where the JSON file is stored.
  • 'db.json': The JSON file storing student data.
  • dbPath: A variable storing the full path to db.json.

This approach ensures compatibility across operating systems (Windows, macOS, Linux).

3.5 Defining the API Endpoints

POST /students – Add a Student

app.post('/students', (req, res) => {
  const newStudent = req.body;

  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) return res.status(500).send('Error reading data');

    const students = JSON.parse(data);
    students.push(newStudent);

    fs.writeFile(dbPath, JSON.stringify(students, null, 2), (err) => {
      if (err) return res.status(500).send('Error saving data');
      res.status(201).send('Student added successfully');
    });
  });
});

Syntax Breakdown:

app.post('/students', (req, res) => { ... });
  • Defines a POST endpoint at /students.
  • req.body: Contains the student data sent in the request.

Reading from the File – fs.readFile

fs.readFile(dbPath, 'utf8', (err, data) => { ... });

Parameters:

  1. dbPath: Full path to the file to be read.

  2. 'utf8': Encoding format to read text as a string.

  3. Callback function:

    • err: Contains an error if reading fails.
    • data: The file content if reading succeeds.

Parsing the File Content

const students = JSON.parse(data);
  • Converts the JSON string into a JavaScript array.

Writing to the File – fs.writeFile

fs.writeFile(dbPath, JSON.stringify(students, null, 2), (err) => { ... });

Parameters:

  1. dbPath: Path to the file to write.

  2. JSON.stringify(students, null, 2):

    • Converts the students array back to a JSON string.
    • null: Placeholder for a replacer function (not used here).
    • 2: Adds indentation for readability.
  3. Callback function:

    • err: Contains an error if the write fails.

GET /students – List All Students

app.get('/students', (req, res) => {
  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) return res.status(500).send('Error reading data');

    const students = JSON.parse(data);
    res.json(students);
  });
});
  • Reads the contents of db.json.
  • Parses the content to an array.
  • Sends it back as a JSON response.

Example Data in db.json

[
  {
    "name": "Alice",
    "email": "alice@example.com"
  },
  {
    "name": "Bob",
    "email": "bob@example.com"
  }
]

Complete Code – server.js (End of Section 3)

// Import required modules
const express = require('express');
const fs = require('fs');
const path = require('path');

// Initialize Express app
const app = express();
const PORT = 3000;

// Middleware to parse incoming JSON data
app.use(express.json());

// Define the path to the JSON file
const dbPath = path.join(__dirname, 'db', 'db.json');

// Root endpoint - returns a welcome message
app.get('/', (req, res) => {
  res.send('Welcome to the Student Management API');
});

// POST /students - Add a new student
app.post('/students', (req, res) => {
  const newStudent = req.body;

  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) {
      return res.status(500).send('Error reading data');
    }

    const students = JSON.parse(data);
    students.push(newStudent);

    fs.writeFile(dbPath, JSON.stringify(students, null, 2), (err) => {
      if (err) {
        return res.status(500).send('Error saving data');
      }
      res.status(201).send('Student added successfully');
    });
  });
});

// GET /students - Retrieve all students
app.get('/students', (req, res) => {
  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) {
      return res.status(500).send('Error reading data');
    }

    const students = JSON.parse(data);
    res.json(students);
  });
});

// Start the server
app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});

Contents of db/db.json

Create a file named db.json inside a folder called db and initialize it with:

[]

This file stores the student records as an array.

4. Testing the API

This section explains how to test your API using cURL, a command-line tool for making HTTP requests.

We will test three endpoints:

  1. GET / – Basic welcome message
  2. GET /students – Retrieve all students
  3. POST /students – Add a new student

Make sure your server is running by executing:

node server.js

4.1 Testing with cURL (Command-Line Tool)

1. GET / – Welcome Message

curl http://localhost:3000/

Explanation:

  • curl: The command-line tool used to make HTTP requests.

  • http://localhost:3000/: The full URL of the API’s root endpoint.

    • localhost: Refers to your local machine.
    • 3000: The port number your server is running on.
    • /: The root path, which returns the welcome message.

📘 Expected output:

Welcome to the Student Management API

2. GET /students – List All Students

curl http://localhost:3000/students

Explanation:

  • Same as before, but with /students at the end of the URL to access the correct endpoint.

📘 Expected output:

[]

Or a list of students, if any were previously added.

3. POST /students – Add a Student

Use this command to add a student with name and email:

curl -X POST http://localhost:3000/students -H "Content-Type: application/json" -d "{\"name\": \"Alice\", \"email\": \"alice@example.com\"}"

Word-by-word explanation:

  • curl: Starts the HTTP request.

  • -X POST: Specifies the HTTP method (POST).

  • http://localhost:3000/students: URL of the endpoint that accepts new student data.

  • -H "Content-Type: application/json": Adds a header to specify the type of data being sent (JSON).

  • -d "{\"name\": \"Alice\", \"email\": \"alice@example.com\"}":

    • -d: Sends data in the request body.
    • The string inside quotes is a JSON object.
    • \": Escaped double quotes are required in the shell to pass a valid JSON string.

📘 Expected output:

Student added successfully

Repeat: GET /students – Verify the Student Record

curl http://localhost:3000/students

📘 Expected Output (formatted for readability):

[
  {
    "name": "Alice",
    "email": "alice@example.com"
  }
]

Explanation:

  • This command retrieves the updated contents of the db.json file.
  • Since you previously added Alice via POST, she now appears in the result.

If you send multiple POST requests, the list will continue to grow.

4.2 Testing with Postman (GUI-based Tool)

Postman provides a visual interface for sending HTTP requests to your API. It's especially helpful if you're new to APIs or want to avoid dealing with syntax in the terminal.

1. GET / – Welcome Message

Steps:

  1. Open Postman.

  2. In the top-left, select the method as GET.

  3. Enter the URL:

    http://localhost:3000/
    
  4. Click Send.

📘 Expected response:

Welcome to the Student Management API

2. GET /students – List All Students

Steps:

  1. Create a new request.

  2. Select method: GET.

  3. URL:

    http://localhost:3000/students
    
  4. Click Send.

📘 Expected response:

[
  {
    "name": "Alice",
    "email": "alice@example.com"
  }
]

This returns all student records saved in db.json.

3. POST /students – Add a New Student

Steps:

  1. Create a new request.

  2. Select method: POST.

  3. URL:

    http://localhost:3000/students
    
  4. Go to the Body tab.

  5. Select raw, then choose JSON from the dropdown menu.

  6. Enter JSON data like this:

{
  "name": "Bob",
  "email": "bob@example.com"
}
  1. Click Send.

📘 Expected response:

Student added successfully

4. Confirm the Student Was Added

Repeat the GET /students request.

📘 Expected output (after adding Bob):

[
  {
    "name": "Alice",
    "email": "alice@example.com"
  },
  {
    "name": "Bob",
    "email": "bob@example.com"
  }
]

Postman also allows you to save and organize requests in collections, which can be helpful as your API grows.

5. Extending the API (Optional Advanced Section)

Now that the basic API is working, we can extend its functionality to support more common operations such as updating, deleting, or retrieving individual student records. These improvements follow REST principles and make the system more complete.

This section includes:

  1. Adding unique IDs to each student
  2. Retrieving a student by ID (GET /students/:id)
  3. Updating a student by ID (PUT /students/:id)
  4. Deleting a student by ID (DELETE /students/:id)
  5. Validating input data
  6. Improving error handling

5.1 Adding Unique IDs to Each Student

To perform operations like update and delete, each student needs a unique identifier.

We'll generate a simple ID using Date.now().

Update in POST /students endpoint:

Replace the current POST /students logic with the following:

app.post('/students', (req, res) => {
  const newStudent = {
    id: Date.now(), // Add a unique ID
    ...req.body
  };

  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) return res.status(500).send('Error reading data');

    const students = JSON.parse(data);
    students.push(newStudent);

    fs.writeFile(dbPath, JSON.stringify(students, null, 2), (err) => {
      if (err) return res.status(500).send('Error saving data');
      res.status(201).send('Student added successfully');
    });
  });
});

Each student now has a unique id field in addition to name and email.

Example stored data:

[
  {
    "id": 1715000000000,
    "name": "Alice",
    "email": "alice@example.com"
  }
]

5.2 GET /students/:id – Retrieve a Student by ID

This endpoint allows clients to request details of a single student by their unique ID.

Implementation

Add the following route to your server.js file:

app.get('/students/:id', (req, res) => {
  const studentId = parseInt(req.params.id, 10); // Convert ID from string to number

  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) return res.status(500).send('Error reading data');

    const students = JSON.parse(data);
    const student = students.find(s => s.id === studentId);

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

    res.json(student);
  });
});

Syntax Breakdown

app.get('/students/:id', ...)
  • Defines a dynamic route where :id is a route parameter.
  • Express extracts the value from the URL and makes it available via req.params.id.
const studentId = parseInt(req.params.id, 10);
  • Converts the id parameter from a string to a number using base 10.
const student = students.find(s => s.id === studentId);
  • Searches for a student with the matching ID.
if (!student) return res.status(404).send('Student not found');
  • If no match is found, return a 404 status code.

Test with cURL

Replace 123456 with an actual ID from your db.json:

curl http://localhost:3000/students/123456

📘 Expected output:

{
  "id": 123456,
  "name": "Alice",
  "email": "alice@example.com"
}

If the student doesn’t exist:

Student not found

5.3 PUT /students/:id – Update a Student by ID

This endpoint allows updating the name or email of a specific student using their ID.

Implementation

Add the following code to your server.js:

app.put('/students/:id', (req, res) => {
  const studentId = parseInt(req.params.id, 10);
  const updatedData = req.body;

  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) return res.status(500).send('Error reading data');

    let students = JSON.parse(data);
    const index = students.findIndex(s => s.id === studentId);

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

    // Merge existing student with updated data
    students[index] = { ...students[index], ...updatedData };

    fs.writeFile(dbPath, JSON.stringify(students, null, 2), (err) => {
      if (err) return res.status(500).send('Error saving data');
      res.send('Student updated successfully');
    });
  });
});

Syntax Breakdown

app.put('/students/:id', ...)
  • Defines a route that listens for PUT requests with an ID.
const studentId = parseInt(req.params.id, 10);
  • Parses the id from the URL as a number.
const updatedData = req.body;
  • Grabs the data from the request body, which may include updated name, email, or both.
const index = students.findIndex(s => s.id === studentId);
  • Finds the index of the student to be updated.
students[index] = { ...students[index], ...updatedData };
  • Merges the original student object with the new data, preserving unchanged fields.

Test with cURL

Replace 123456 with a valid ID:

curl -X PUT http://localhost:3000/students/123456 \
  -H "Content-Type: application/json" \
  -d "{\"name\": \"Alice Johnson\"}"

📘 Expected response:

Student updated successfully

Then verify the change with:

curl http://localhost:3000/students/123456

5.4 DELETE /students/:id – Delete a Student by ID

This endpoint removes a student from the list based on their unique ID.

Implementation

Add the following code to your server.js file:

app.delete('/students/:id', (req, res) => {
  const studentId = parseInt(req.params.id, 10);

  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) return res.status(500).send('Error reading data');

    let students = JSON.parse(data);
    const filteredStudents = students.filter(s => s.id !== studentId);

    if (students.length === filteredStudents.length) {
      return res.status(404).send('Student not found');
    }

    fs.writeFile(dbPath, JSON.stringify(filteredStudents, null, 2), (err) => {
      if (err) return res.status(500).send('Error saving data');
      res.send('Student deleted successfully');
    });
  });
});

Syntax Breakdown

app.delete('/students/:id', ...)
  • Defines a route for HTTP DELETE requests where :id is a route parameter.
const filteredStudents = students.filter(s => s.id !== studentId);
  • Returns a new array excluding the student with the given ID.
if (students.length === filteredStudents.length)
  • If no student was removed, it means the ID didn’t match any record.

Test with cURL

Replace 123456 with an actual ID:

curl -X DELETE http://localhost:3000/students/123456

📘 Expected response:

Student deleted successfully

Then confirm with:

curl http://localhost:3000/students

The student should no longer appear in the list.

Complete server.js Code (End of Section 5.4)

// Import required modules
const express = require('express');
const fs = require('fs');
const path = require('path');

// Initialize Express app
const app = express();
const PORT = 3000;

// Middleware to parse incoming JSON data
app.use(express.json());

// Define path to db.json
const dbPath = path.join(__dirname, 'db', 'db.json');

// Root endpoint
app.get('/', (req, res) => {
  res.send('Welcome to the Student Management API');
});

// POST /students – Add a new student with a unique ID
app.post('/students', (req, res) => {
  const newStudent = {
    id: Date.now(), // Generate unique ID
    ...req.body
  };

  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) return res.status(500).send('Error reading data');

    const students = JSON.parse(data);
    students.push(newStudent);

    fs.writeFile(dbPath, JSON.stringify(students, null, 2), (err) => {
      if (err) return res.status(500).send('Error saving data');
      res.status(201).send('Student added successfully');
    });
  });
});

// GET /students – Retrieve all students
app.get('/students', (req, res) => {
  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) return res.status(500).send('Error reading data');

    const students = JSON.parse(data);
    res.json(students);
  });
});

// GET /students/:id – Retrieve a student by ID
app.get('/students/:id', (req, res) => {
  const studentId = parseInt(req.params.id, 10);

  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) return res.status(500).send('Error reading data');

    const students = JSON.parse(data);
    const student = students.find(s => s.id === studentId);

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

    res.json(student);
  });
});

// PUT /students/:id – Update a student by ID
app.put('/students/:id', (req, res) => {
  const studentId = parseInt(req.params.id, 10);
  const updatedData = req.body;

  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) return res.status(500).send('Error reading data');

    let students = JSON.parse(data);
    const index = students.findIndex(s => s.id === studentId);

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

    // Merge existing student data with updated fields
    students[index] = { ...students[index], ...updatedData };

    fs.writeFile(dbPath, JSON.stringify(students, null, 2), (err) => {
      if (err) return res.status(500).send('Error saving data');
      res.send('Student updated successfully');
    });
  });
});

// DELETE /students/:id – Delete a student by ID
app.delete('/students/:id', (req, res) => {
  const studentId = parseInt(req.params.id, 10);

  fs.readFile(dbPath, 'utf8', (err, data) => {
    if (err) return res.status(500).send('Error reading data');

    let students = JSON.parse(data);
    const filteredStudents = students.filter(s => s.id !== studentId);

    if (students.length === filteredStudents.length) {
      return res.status(404).send('Student not found');
    }

    fs.writeFile(dbPath, JSON.stringify(filteredStudents, null, 2), (err) => {
      if (err) return res.status(500).send('Error saving data');
      res.send('Student deleted successfully');
    });
  });
});

// Start the server
app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});