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 dataPOST
– to create dataPUT
– to update dataDELETE
– 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:
Method | Endpoint | Description |
---|---|---|
GET | / | Returns a basic welcome message |
GET | /students | Retrieves a list of all students |
POST | /students | Adds 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.
- Download from: https://www.postman.com/downloads/
- Install and open the application.
- An account is optional for basic usage.
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:
- Download the installer from: https://curl.se/windows/
- Choose the appropriate version (usually 64-bit with SSL support).
- Extract the ZIP archive.
- Add the folder containing
curl.exe
to your system’sPATH
environment variable. - 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 namedexpress
.
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
-
Open the integrated terminal by selecting
Terminal > New Terminal
from the top menu. -
In the terminal, run the following command to start the server:
node server.js
-
If the server starts successfully, you will see this message in the terminal:
Server is running on http://localhost:3000
-
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 asreq.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 (whereserver.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 todb.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:
-
dbPath
: Full path to the file to be read. -
'utf8'
: Encoding format to read text as a string. -
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:
-
dbPath
: Path to the file to write. -
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.
- Converts the
-
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:
GET /
– Basic welcome messageGET /students
– Retrieve all studentsPOST /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:
-
Open Postman.
-
In the top-left, select the method as
GET
. -
Enter the URL:
http://localhost:3000/
-
Click Send.
📘 Expected response:
Welcome to the Student Management API
2. GET /students
– List All Students
Steps:
-
Create a new request.
-
Select method:
GET
. -
URL:
http://localhost:3000/students
-
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:
-
Create a new request.
-
Select method:
POST
. -
URL:
http://localhost:3000/students
-
Go to the Body tab.
-
Select raw, then choose JSON from the dropdown menu.
-
Enter JSON data like this:
{ "name": "Bob", "email": "bob@example.com" }
- 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:
- Adding unique IDs to each student
- Retrieving a student by ID (
GET /students/:id
) - Updating a student by ID (
PUT /students/:id
) - Deleting a student by ID (
DELETE /students/:id
) - Validating input data
- 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}`); });