Sign in
Topics
Build 10x products in minutes by chatting with AI - beyond just a prototype.
This article provides a step-by-step guide to building a secure API with Node.js and Express. It covers key practices like authentication, input validation, and error handling. Learn how to protect your app from common threats while keeping performance and scalability in check.
Is your API strong enough to stand up to today’s security threats?
From banking apps to food delivery services, APIs now form the core of most digital experiences. But with that convenience comes risk. Weak access controls, sloppy input checks, and open endpoints can make your app an easy target. So, building a secure API isn't just a nice-to-have—it’s a must.
That’s where Node.js and Express prove useful. The right practices help you create fast, scalable, and secure APIs that hold up in real-world use.
This blog is your step-by-step guide to building a secure API with Node.js and Express. You’ll learn how to handle authentication, validate inputs, manage errors, and more—all while keeping your app safe. Ready to learn what makes an API production-ready?
Let’s begin.
Before writing a single line of code, it’s important to understand the core components you're using.
Component | Description |
---|---|
Node.js | A cross platform runtime environment for executing JavaScript on the server |
Express.js | A lightweight web framework for Node.js to manage http requests, routing, and middleware |
REST API | A stateless architectural style using HTTP methods to manipulate json data resources |
Using the JavaScript programming language, you will write the backend logic in a .js file. With Express, your API becomes a scalable part of your web applications.
Use the following command in the command prompt to create a new Node.js project:
1npm init -y 2npm install express
Then, create a server.js or index.js file:
1// index.js 2const express = require('express'); 3const app = express(); // express app 4const PORT = 3000; 5 6app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
The app is the application object. It manages the API's lifecycle, including HTTP requests, response objects, and routing.
Organize your codebase:
1project/ 2│ 3├── routes/ 4│ └── userRoutes.js 5├── controllers/ 6│ └── userController.js 7├── middleware/ 8│ └── authMiddleware.js 9├── public/ <-- directory named public 10├── views/ 11│ └── index.html 12└── index.js
Use a directory named public to serve static files like images, stylesheets, and JavaScript files:
1app.use(express.static('public')); // serve static files
Create a basic route in your JS file:
1app.get('/api/users', (req, res) => { 2 res.json([{ id: 1, name: "Alice" }]); // json response 3});
Use the following code to parse incoming JSON post data:
1app.use(express.json()); // parsing JSON payloads
1const express = require('express'); 2const router = express.Router(); 3 4router.get('/', (req, res) => { 5 res.status(200).json({ message: 'GET all users' }); 6});
JWT helps protect secure REST API endpoints.
1const jwt = require('jsonwebtoken'); 2const token = jwt.sign({ userId: 123 }, 'secretKey', { expiresIn: '1h' });
Use the following middleware to protect routes:
1const auth = (req, res, next) => { 2 const token = req.headers.authorization?.split(' ')[1]; 3 if (!token) return res.status(401).json({ message: 'Access denied' }); 4 5 try { 6 const verified = jwt.verify(token, 'secretKey'); 7 req.user = verified; 8 next(); 9 } catch (err) { 10 res.status(400).json({ message: 'Invalid Token' }); 11 } 12};
Apply to any secure REST API route:
1app.get('/api/private', auth, (req, res) => { 2 res.json({ message: 'Secure data' }); 3});
Validate user input to prevent injection and malformed data using Express Validator.
1npm install express-validator
1const { body, validationResult } = require('express-validator'); 2 3app.post( 4 '/api/register', 5 [ 6 body('email').isEmail().withMessage('Provide a valid email'), // valid email 7 body('password').isLength({ min: 6 }) 8 ], 9 (req, res) => { 10 const errors = validationResult(req); 11 if (!errors.isEmpty()) return res.status(400).json(errors.array()); 12 13 res.json({ message: 'User registered' }); 14 } 15);
Input validation is a core part of building a secure REST API and defending against attacks.
Centralize error handling in middleware:
1app.use((err, req, res, next) => { 2 console.error(err.stack); // log error 3 res.status(500).json({ error: 'Something went wrong' }); // following response 4});
Use the console or logging libraries like winston to log errors, HTTP requests, and failed post data attempts.
Add security measures using the helmet and express-rate-limit packages:
1npm install helmet express-rate-limit
1const helmet = require('helmet'); 2const rateLimit = require('express-rate-limit'); 3 4app.use(helmet()); // secure headers 5 6const limiter = rateLimit({ 7 windowMs: 15 * 60 * 1000, 8 max: 100 9}); 10app.use(limiter); // limit requests per IP
Connect to a MongoDB database:
1npm install mongoose
1const mongoose = require('mongoose'); 2mongoose.connect('mongodb://localhost:27017/users', { 3 useNewUrlParser: true, 4 useUnifiedTopology: true 5});
Use Mongoose schemas to validate and sanitize post data before saving.
Use res.render for rendering HTML views with a templating engine like Pug or EJS:
1app.set('view engine', 'ejs'); 2app.get('/', (req, res) => res.render('index', { title: 'Home Page' }));
Here’s a simple Mermaid diagram of the req-res flow in an express app:
Every unsecured endpoint, misvalidated input, or exposed credential increases the risk of a breach. Throughout this guide, we’ve shown how building a secure API with Node.js and Express helps you address these vulnerabilities head-on through proper middleware configuration, strict input validation, secure token-based authentication, and structured error handling.
With APIs at the heart of most web and mobile applications, securing them is no longer optional—it's a core requirement for trust, compliance, and business continuity. Node.js and the Express framework give you the flexibility and control to implement these security measures with precision and scalability.
Start applying these best practices today. Review your codebase, patch weak spots, and elevate your Express app from functional to fortified.