Web DevelopmentNổi bật
Node.js Express MongoDB Setup
Vu Van F
Backend Developer
7 tháng 1, 202507-01
12p
1,298 lượt xem1k
#Node.js#Express#MongoDB#Backend#API
Node.js Express MongoDB Setup
Xây dựng backend robust với Node.js, Express và MongoDB.
Project Setup
JSON
// package.json
{
"name": "nodejs-api",
"version": "1.0.0",
"description": "Node.js API with Express and MongoDB",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"test": "jest"
},
"dependencies": {
"express": "^4.18.2",
"mongoose": "^8.0.0",
"bcryptjs": "^2.4.3",
"jsonwebtoken": "^9.0.2",
"cors": "^2.8.5",
"helmet": "^7.1.0",
"express-rate-limit": "^7.1.5",
"joi": "^17.11.0"
},
"devDependencies": {
"nodemon": "^3.0.1",
"jest": "^29.7.0"
}
}
Server Setup và Middleware
JavaScript
// server.js
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
require('dotenv').config();
const app = express();
// Security middleware
app.use(helmet());
app.use(cors({
origin: process.env.FRONTEND_URL || 'http://localhost:3000',
credentials: true
}));
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP'
});
app.use('/api/', limiter);
// Body parser
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// MongoDB connection
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/myapp', {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log('MongoDB connected'))
.catch(err => console.error('MongoDB connection error:', err));
// Routes
app.use('/api/auth', require('./routes/auth'));
app.use('/api/users', require('./routes/users'));
app.use('/api/posts', require('./routes/posts'));
// Global error handler
app.use((err, req, res, next) => {
console.error(err.stack);
if (err.name === 'ValidationError') {
return res.status(400).json({
error: 'Validation Error',
details: Object.values(err.errors).map(e => e.message)
});
}
if (err.name === 'CastError') {
return res.status(400).json({
error: 'Invalid ID format'
});
}
res.status(err.statusCode || 500).json({
error: err.message || 'Internal Server Error'
});
});
// 404 handler
app.use('*', (req, res) => {
res.status(404).json({ error: 'Route not found' });
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Mongoose Models
JavaScript
// models/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const userSchema = new mongoose.Schema({
username: {
type: String,
required: [true, 'Username is required'],
unique: true,
minlength: 3,
maxlength: 50
},
email: {
type: String,
required: [true, 'Email is required'],
unique: true,
lowercase: true,
match: [/^w+([.-]?w+)*@w+([.-]?w+)*(.w{2,3})+$/, 'Invalid email']
},
password: {
type: String,
required: [true, 'Password is required'],
minlength: 6
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user'
},
profile: {
firstName: String,
lastName: String,
avatar: String,
bio: String
},
isActive: {
type: Boolean,
default: true
}
}, {
timestamps: true,
toJSON: {
transform: function(doc, ret) {
delete ret.password;
return ret;
}
}
});
// Hash password before saving
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
try {
const salt = await bcrypt.genSalt(12);
this.password = await bcrypt.hash(this.password, salt);
next();
} catch (error) {
next(error);
}
});
// Compare password method
userSchema.methods.comparePassword = async function(candidatePassword) {
return bcrypt.compare(candidatePassword, this.password);
};
// Static method to find active users
userSchema.statics.findActive = function() {
return this.find({ isActive: true });
};
module.exports = mongoose.model('User', userSchema);
Express Routes với Validation
JavaScript
// routes/auth.js
const express = require('express');
const jwt = require('jsonwebtoken');
const Joi = require('joi');
const User = require('../models/User');
const auth = require('../middleware/auth');
const router = express.Router();
// Validation schemas
const registerSchema = Joi.object({
username: Joi.string().min(3).max(50).required(),
email: Joi.string().email().required(),
password: Joi.string().min(6).required(),
firstName: Joi.string().min(2),
lastName: Joi.string().min(2)
});
const loginSchema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().required()
});
// Register
router.post('/register', async (req, res, next) => {
try {
const { error, value } = registerSchema.validate(req.body);
if (error) {
return res.status(400).json({
error: 'Validation failed',
details: error.details.map(d => d.message)
});
}
// Check if user exists
const existingUser = await User.findOne({
$or: [
{ email: value.email },
{ username: value.username }
]
});
if (existingUser) {
return res.status(409).json({
error: 'User already exists'
});
}
// Create user
const user = new User({
username: value.username,
email: value.email,
password: value.password,
profile: {
firstName: value.firstName,
lastName: value.lastName
}
});
await user.save();
// Generate JWT
const token = jwt.sign(
{ userId: user._id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.status(201).json({
message: 'User created successfully',
token,
user
});
} catch (error) {
next(error);
}
});
// Login
router.post('/login', async (req, res, next) => {
try {
const { error, value } = loginSchema.validate(req.body);
if (error) {
return res.status(400).json({
error: 'Validation failed',
details: error.details.map(d => d.message)
});
}
// Find user
const user = await User.findOne({ email: value.email });
if (!user || !user.isActive) {
return res.status(401).json({
error: 'Invalid credentials'
});
}
// Check password
const isValidPassword = await user.comparePassword(value.password);
if (!isValidPassword) {
return res.status(401).json({
error: 'Invalid credentials'
});
}
// Generate JWT
const token = jwt.sign(
{ userId: user._id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.json({
message: 'Login successful',
token,
user
});
} catch (error) {
next(error);
}
});
// Get current user
router.get('/me', auth, async (req, res, next) => {
try {
const user = await User.findById(req.user.userId);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json({ user });
} catch (error) {
next(error);
}
});
module.exports = router;
Authentication Middleware
JavaScript
// middleware/auth.js
const jwt = require('jsonwebtoken');
const User = require('../models/User');
const auth = async (req, res, next) => {
try {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'Access denied. No token provided.' });
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findById(decoded.userId);
if (!user || !user.isActive) {
return res.status(401).json({ error: 'Invalid token.' });
}
req.user = decoded;
next();
} catch (error) {
if (error.name === 'JsonWebTokenError') {
return res.status(401).json({ error: 'Invalid token.' });
}
if (error.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token expired.' });
}
res.status(500).json({ error: 'Server error.' });
}
};
// Admin authorization middleware
const adminAuth = (req, res, next) => {
if (req.user.role !== 'admin') {
return res.status(403).json({ error: 'Access denied. Admin only.' });
}
next();
};
module.exports = { auth, adminAuth };
Security tip: Luôn validate input data và sử dụng HTTPS trong production!
Kết luận
Node.js với Express và MongoDB tạo nên backend stack mạnh mẽ và scalable.
Vu Van F
Backend Developer chuyên Node.js và microservices.