We building a PRODUCTION GRADE backend .
We'll now extend the Node.js backend we started earlier in PART 1, and add all these advanced features :
🛠 Add these features step-by-step:
1️⃣ Global Error Handler Middleware
➔ src/middlewares/errorMiddleware.js
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
const statusCode = res.statusCode ? res.statusCode : 500;
res.status(statusCode).json({
message: err.message || 'Internal Server Error',
stack: process.env.NODE_ENV === 'production' ? null : err.stack,
});
};
module.exports = { errorHandler };
✅ In src/app.js
add at bottom:
const { errorHandler } = require('./middlewares/errorMiddleware');
// After all routes
app.use(errorHandler);
2️⃣ Refresh Token & Access Token System
We'll create:
- Access Token (short expiry like 15 minutes)
- Refresh Token (long expiry like 7 days)
✅ Update authController.js
:
// Issue Tokens
const issueTokens = (userId) => {
const accessToken = jwt.sign({ id: userId }, process.env.JWT_SECRET, { expiresIn: '15m' });
const refreshToken = jwt.sign({ id: userId }, process.env.JWT_REFRESH_SECRET, { expiresIn: '7d' });
return { accessToken, refreshToken };
};
// Login Controller Updated
exports.login = async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.findOne({ email });
if (!user) return res.status(400).json({ message: 'Invalid credentials' });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).json({ message: 'Invalid credentials' });
const tokens = issueTokens(user._id);
res.status(200).json(tokens);
} catch (err) {
res.status(500).json({ message: err.message });
}
};
// Refresh Token Controller
exports.refreshToken = async (req, res) => {
const { token } = req.body;
if (!token) return res.status(401).json({ message: 'No refresh token provided' });
try {
const decoded = jwt.verify(token, process.env.JWT_REFRESH_SECRET);
const tokens = issueTokens(decoded.id);
res.status(200).json(tokens);
} catch (err) {
res.status(403).json({ message: 'Invalid refresh token' });
}
};
✅ Update src/routes/authRoutes.js
:
const { register, login, refreshToken } = require('../controllers/authController');
router.post('/refresh-token', refreshToken);
✅ Update .env
:
JWT_REFRESH_SECRET=your_refresh_secret_key
3️⃣ DTOs (Data Transfer Objects)
➔ Create src/dto/user.dto.js
exports.userRegisterDTO = (req) => {
return {
name: req.body.name,
email: req.body.email,
password: req.body.password
};
};
exports.userLoginDTO = (req) => {
return {
email: req.body.email,
password: req.body.password
};
};
✅ Then use in authController.js
:
const { userRegisterDTO, userLoginDTO } = require('../dto/user.dto');
// Inside register
const userDTO = userRegisterDTO(req);
// Inside login
const loginDTO = userLoginDTO(req);
4️⃣ Rate Limiting
➔ Install package:
npm install express-rate-limit
➔ Create src/middlewares/rateLimiter.js
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 min
max: 100, // 100 requests per 15 minutes
message: "Too many requests from this IP, please try again later",
});
module.exports = { limiter };
✅ Add to app.js
:
const { limiter } = require('./middlewares/rateLimiter');
app.use(limiter);
5️⃣ Swagger Documentation
➔ Install:
npm install swagger-jsdoc swagger-ui-express
➔ Create src/swagger/swaggerConfig.js
:
const swaggerJsDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'Node.js CRUD API',
version: '1.0.0',
description: 'Simple CRUD API with JWT Auth'
},
servers: [
{
url: 'http://localhost:5000'
}
]
},
apis: ['./src/routes/*.js'], // Path to your API files
};
const swaggerSpec = swaggerJsDoc(options);
const swaggerDocs = (app) => {
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
};
module.exports = swaggerDocs;
✅ In src/app.js
:
const swaggerDocs = require('./swagger/swaggerConfig');
swaggerDocs(app);
✅ Example in src/routes/authRoutes.js
(add comment above API):
/**
* @swagger
* /api/auth/register:
* post:
* summary: Register new user
* tags: [Auth]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* email:
* type: string
* password:
* type: string
* responses:
* 201:
* description: User registered successfully
* 400:
* description: Bad request
*/
router.post('/register', [...], register);
Swagger now at: http://localhost:5000/api-docs
6️⃣ Logging (Morgan + Winston)
➔ Install:
npm install morgan winston
✅ In src/app.js
:
const morgan = require('morgan');
const winston = require('winston');
// Morgan HTTP logger
app.use(morgan('dev'));
// Winston custom logger
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
],
});
// Global logger usage example
app.use((req, res, next) => {
logger.info(`${req.method} ${req.url}`);
next();
});
7️⃣ Dockerfile
➔ Create Dockerfile
in project root:
# Use Node.js LTS base image
FROM node:18
# Set working directory
WORKDIR /app
# Copy package.json and install dependencies
COPY package*.json ./
RUN npm install
# Copy all files
COPY . .
# Expose port
EXPOSE 5000
# Run the app
CMD ["npm", "start"]
✅ Create .dockerignore
node_modules
npm-debug.log
✅ Build & Run Docker:
docker build -t node-crud-app .
docker run -p 5000:5000 node-crud-app
📋 Updated .env example
PORT=5000
MONGO_URI=your_mongo_uri
JWT_SECRET=your_jwt_secret
JWT_REFRESH_SECRET=your_jwt_refresh_secret
JWT_EXPIRES_IN=15m
🏆 Congratulations!
👉 Now your backend:
- 🔥 Has Global error handling
- 🔥 Has Access + Refresh tokens
- 🔥 Has Rate limiting security
- 🔥 Has DTOs (clean request handling)
- 🔥 Has API documentation (Swagger)
- 🔥 Has Logging (Morgan + Winston)
- 🔥 Is Dockerized for production deployment
🚀 Final API Endpoints
Method | URL | Description |
---|---|---|
POST | /api/auth/register |
Register user |
POST | /api/auth/login |
Login user |
POST | /api/auth/refresh-token |
Get new tokens |
GET |
/api/users (protected) |
List all users |
GET | /api-docs |
API Docs |
Top comments (0)