Building a Secure REST API with Node.js and Express
Modern web applications often depend on APIs to communicate between the frontend, backend, and databases. Whether you're building a dashboard, mobile application, or SaaS product, understanding how REST APIs work is an important backend skill.
In this article, we'll build a simple REST API using Node.js and Express.js, while following a structure that can later be extended with authentication and a database.
What We're Building
Our API will have endpoints for managing users:
GET /api/users
GET /api/users/:id
POST /api/users
PUT /api/users/:id
DELETE /api/users/:id
The basic architecture looks like this:
Client
โ
โผ
Express Server
โ
โโโ Routes
โ
โโโ Controllers
โ
โโโ Middleware
โ
โผ
Database
- Create the Project
Start by creating a new Node.js project:
mkdir node-rest-api cd node-rest-api npm init -y
Install Express:
npm install express
For development, you can also install Nodemon:
npm install --save-dev nodemon 2. Create the Server
Create an app.js file:
const express = require("express");
const app = express(); const PORT = 3000;
app.use(express.json());
app.get("/", (req, res) => { res.json({ message: "API is running ๐" }); });
app.listen(PORT, () => { console.log(Server running on port ${PORT}); });
Run it:
node app.js
Visit:
http://localhost:3000
You should get:
{ "message": "API is running ๐" } 3. Create Some Data
For now, we'll use an array instead of a database.
let users = [ { id: 1, name: "Ajnas", email: "ajnas@example.com" }, { id: 2, name: "John", email: "john@example.com" } ]; 4. GET All Users
We can create an endpoint that returns all users:
app.get("/api/users", (req, res) => { res.json(users); });
Request:
GET /api/users
Response:
[ { "id": 1, "name": "Ajnas", "email": "ajnas@example.com" }, { "id": 2, "name": "John", "email": "john@example.com" } ] 5. GET a Single User
Route parameters allow us to retrieve a specific user.
app.get("/api/users/:id", (req, res) => { const id = Number(req.params.id);
const user = users.find(user => user.id === id);
if (!user) {
return res.status(404).json({
message: "User not found"
});
}
res.json(user);
});
Now:
GET /api/users/1
returns:
{ "id": 1, "name": "Ajnas", "email": "ajnas@example.com" }
6. Creating a User
For creating resources, we use POST.
app.post("/api/users", (req, res) => { const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({
message: "Name and email are required"
});
}
const newUser = {
id: users.length + 1,
name,
email
};
users.push(newUser);
res.status(201).json(newUser);
});
The request body:
{ "name": "Charlie", "email": "charlie@example.com" }
The API responds with:
{ "id": 3, "name": "Charlie", "email": "charlie@example.com" }
7. Updating a User
We can use PUT to update an existing resource:
app.put("/api/users/:id", (req, res) => { const id = Number(req.params.id);
const user = users.find(user => user.id === id);
if (!user) {
return res.status(404).json({
message: "User not found"
});
}
user.name = req.body.name ?? user.name;
user.email = req.body.email ?? user.email;
res.json(user);
});
This lets us update only the fields we provide.
- Deleting a User
Finally, we can implement DELETE:
app.delete("/api/users/:id", (req, res) => { const id = Number(req.params.id);
const exists = users.some(user => user.id === id);
if (!exists) {
return res.status(404).json({
message: "User not found"
});
}
users = users.filter(user => user.id !== id);
res.json({
message: "User deleted successfully"
});
}); A Better Project Structure
Once an application grows, keeping everything inside app.js becomes difficult.
A more maintainable structure could look like:
node-rest-api/
โ
โโโ controllers/
โ โโโ userController.js
โ
โโโ routes/
โ โโโ userRoutes.js
โ
โโโ middleware/
โ โโโ errorHandler.js
โ
โโโ models/
โ โโโ userModel.js
โ
โโโ app.js
โโโ package.json
โโโ .env
This separation makes the application easier to maintain and extend.
What's Next?
The array we're using isn't suitable for a real production application.
The next step would be connecting the API to a database such as PostgreSQL or MongoDB.
From there, we could add:
Database
โ
Authentication
โ
Authorization
โ
Input Validation
โ
Error Handling
โ
Rate Limiting
โ
Logging
โ
Production Deployment
That's where a simple REST API starts becoming a real backend application.
Final Thoughts
Building a REST API is one of those projects that looks simple at first but teaches a lot about backend development.
You learn how HTTP methods work, how requests and responses are structured, how routing works, how to handle errors, and eventually how different parts of a backend application communicate.
I'm still exploring backend development myself, and projects like this are helping me understand what happens behind the interface of the applications we use every day.
More of my projects and experiments:

