Node Js

How to protect a node JS Api ? – complete guide

authentication, authorization, validation, rate limiting, CORS, headers, logging, database safety, deployment

Complete guide to protect Node.js API

Main security layers:

Client

CORS

Rate Limit

Helmet Security Headers

Authentication

Authorization

Input Validation

Controller

Database Protection

Express officially recommends security headers with helmet, safe cookies, dependency updates, rate limiting, and avoiding unsafe defaults. OWASP also strongly recommends allow-list input validation to prevent injection attacks.


1. Install security packages

npm install helmet cors express-rate-limit dotenv bcryptjs jsonwebtoken express-validator mongoose mongo-sanitize hpp morgan

2. Basic secure Express setup

const express = require("express");
const cors = require("cors");
const helmet = require("helmet");
const rateLimit = require("express-rate-limit");
const mongoSanitize = require("mongo-sanitize");
const hpp = require("hpp");
const morgan = require("morgan");

const app = express();

app.use(helmet());

app.use(cors({
origin: [
"https://cybotrix.com",
"https://admin.cybotrix.com",
"http://localhost:5173"
],
methods: ["GET", "POST", "PUT", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization"]
}));

app.use(express.json({ limit: "10kb" }));

app.use((req, res, next) => {
req.body = mongoSanitize(req.body);
req.params = mongoSanitize(req.params);
req.query = mongoSanitize(req.query);
next();
});

app.use(hpp());

app.use(morgan("combined"));

CORS only controls whether browsers can read responses; tools like Postman or server-to-server requests do not depend on browser CORS enforcement.


3. Rate limiting

Use this to stop repeated requests, brute-force login attempts, and API abuse. express-rate-limit is designed for limiting repeated requests to public APIs and sensitive endpoints like password reset.

const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: {
success: false,
message: "Too many requests. Please try again later."
}
});

app.use("/api", apiLimiter);

For login route:

const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
message: {
success: false,
message: "Too many login attempts. Try again later."
}
});

app.use("/api/auth/login", loginLimiter);

4. Password hashing

Never store plain passwords.

const bcrypt = require("bcryptjs");

const hashedPassword = await bcrypt.hash(password, 10);

const isMatch = await bcrypt.compare(password, user.password);

5. JWT authentication

Login token generate

const jwt = require("jsonwebtoken");

const token = jwt.sign(
{
id: user._id,
role: user.role
},
process.env.JWT_SECRET,
{
expiresIn: "1d"
}
);

Auth middleware

const jwt = require("jsonwebtoken");

const protect = async (req, res, next) => {
try {
let token = req.headers.authorization;

if (!token || !token.startsWith("Bearer ")) {
return res.status(401).json({
success: false,
message: "Unauthorized. Token missing."
});
}

token = token.split(" ")[1];

const decoded = jwt.verify(token, process.env.JWT_SECRET);

req.user = decoded;

next();
} catch (error) {
return res.status(401).json({
success: false,
message: "Invalid or expired token."
});
}
};

module.exports = protect;

6. Role-based authorization

Authentication means who you are. Authorization means what you are allowed to do. OWASP recommends authorization checks based on business rules and role/permission boundaries.

const allowRoles = (...roles) => {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({
success: false,
message: "Access denied."
});
}

next();
};
};

module.exports = allowRoles;

Use:

router.delete(
"/users/:id",
protect,
allowRoles("admin"),
deleteUser
);

7. Input validation

const { body, validationResult } = require("express-validator");

const validateUser = [
body("name")
.trim()
.notEmpty()
.withMessage("Name is required"),

body("email")
.isEmail()
.withMessage("Valid email is required"),

body("password")
.isLength({ min: 6 })
.withMessage("Password must be minimum 6 characters"),

(req, res, next) => {
const errors = validationResult(req);

if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
errors: errors.array()
});
}

next();
}
];

module.exports = validateUser;

8. Protected route example

const express = require("express");
const router = express.Router();

const protect = require("../middleware/protect");
const allowRoles = require("../middleware/allowRoles");

router.get("/profile", protect, async (req, res) => {
res.json({
success: true,
user: req.user
});
});

router.get(
"/admin-dashboard",
protect,
allowRoles("admin"),
async (req, res) => {
res.json({
success: true,
message: "Admin dashboard data"
});
}
);

module.exports = router;

9. Environment variables

.env

PORT=5000
MONGO_URI=mongodb://127.0.0.1:27017/secure_api
JWT_SECRET=use_strong_secret_key_here
NODE_ENV=production

Never expose this file:

.env
node_modules

10. Secure MongoDB queries

Bad:

const user = await User.findOne(req.body);

Better:

const user = await User.findOne({
email: req.body.email
});

For update:

const allowedFields = ["name", "mobile", "city"];

const updateData = {};

allowedFields.forEach((field) => {
if (req.body[field]) {
updateData[field] = req.body[field];
}
});

await User.findByIdAndUpdate(req.params.id, updateData, {
new: true,
runValidators: true
});

11. Error handling middleware

app.use((err, req, res, next) => {
console.error(err);

res.status(err.statusCode || 500).json({
success: false,
message: err.message || "Server Error"
});
});

12. Final production checklist

Use HTTPS
Use Helmet
Use CORS with allowed domains only
Use rate limiting
Hash passwords with bcrypt
Use JWT expiry
Validate every input
Sanitize MongoDB input
Use role-based access
Hide .env file
Do not expose stack trace
Limit request body size
Update npm packages
Use logging
Use backup for database
Use firewall/security group

Best structure

project/

├── config/
│ └── db.js

├── controllers/
│ └── authController.js

├── middleware/
│ ├── protect.js
│ ├── allowRoles.js
│ └── errorHandler.js

├── models/
│ └── User.js

├── routes/
│ └── authRoutes.js

├── .env
├── server.js
└── package.json

For your admin panel/API, minimum protection should be:

Bearer Token
CORS only for admin domain
Role-based access
Rate limit login
Validate all form data
HTTPS compulsory