Express.js Backend Development Guide
Express.js Backend Development Guide
3. Middleware
4. Request/Response Handling
5. Error Handling
6. Database Integration
9. Environment Configuration
Installation
bash
npm init -y
npm install express
npm install -D nodemon
Basic Server
javascript
// Middleware
app.use(express.json()); // Parse JSON bodies
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded bodies
// Basic route
app.get('/', (req, res) => {
res.json({ message: 'Hello Express!' });
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Project Structure
project/
├── app.js # Main application file
├── routes/ # Route handlers
│ ├── users.js
│ └── auth.js
├── middleware/ # Custom middleware
├── models/ # Database models
├── controllers/ # Business logic
├── config/ # Configuration files
└── public/ # Static files
Basic Routes
javascript
// HTTP Methods
app.get('/users', (req, res) => {
res.json({ users: [] });
});
Route Parameters
javascript
// Path parameters
app.get('/users/:id', (req, res) => {
const { id } = req.params;
res.json({ userId: id });
});
// Multiple parameters
app.get('/users/:id/posts/:postId', (req, res) => {
const { id, postId } = req.params;
res.json({ userId: id, postId });
});
// Query parameters
app.get('/search', (req, res) => {
const { q, limit = 10, offset = 0 } = req.query;
res.json({ query: q, limit: Number(limit), offset: Number(offset) });
});
// Optional parameters
app.get('/posts/:year/:month?', (req, res) => {
const { year, month } = req.params;
res.json({ year, month: month || 'all' });
});
Router Module
javascript
// routes/users.js
const express = require('express');
const router = express.Router();
module.exports = router;
// app.js
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes);
Middleware
Built-in Middleware
javascript
// Parse JSON
app.use(express.json({ limit: '10mb' }));
// CORS middleware
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
Custom Middleware
javascript
// Logging middleware
const logger = (req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next();
};
// Authentication middleware
const authenticate = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
// Verify token logic here
req.user = { id: 1, name: 'John' }; // Mock user
next();
};
if (!requests.has(ip)) {
requests.set(ip, []);
}
recentRequests.push(now);
requests.set(ip, recentRequests);
next();
};
};
// Usage
app.use(logger);
app.use('/api', rateLimit(100, 15 * 60 * 1000));
app.use('/api/protected', authenticate);
javascript
// 404 handler
app.use('*', (req, res) => {
res.status(404).json({ error: 'Route not found' });
});
Request/Response Handling
Request Object
javascript
// Query parameters
const { limit } = req.query;
// Route parameters
const { id } = req.params;
// Headers
const contentType = req.get('Content-Type');
const userAgent = req.get('User-Agent');
Response Object
javascript
// JSON response
res.json(user);
// Set headers
res.set('Content-Type', 'application/json');
res.set('X-Custom-Header', 'value');
// Cookies
res.cookie('sessionId', '123456', { httpOnly: true });
// Redirect
res.redirect('/login');
res.redirect(301, '/new-url');
});
Request Validation
javascript
if (!email || !email.includes('@')) {
errors.push('Valid email is required');
}
if (errors.length > 0) {
return res.status(400).json({ errors });
}
next();
};
Error Handling
// Operational errors
if (err.isOperational) {
return res.status(err.statusCode).json({
status: 'error',
message: err.message
});
}
// Programming errors
res.status(500).json({
status: 'error',
message: 'Something went wrong'
});
});
Database Integration
bash
// Connection
mongoose.connect('mongodb://localhost:27017/myapp', {
useNewUrlParser: true,
useUnifiedTopology: true
});
// User Schema
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
createdAt: { type: Date, default: Date.now }
});
// CRUD Operations
app.get('/api/users', async (req, res) => {
try {
const users = await User.find().select('-password');
res.json(users);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
bash
// User Model
const User = sequelize.define('User', {
name: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
}
});
// Sync database
sequelize.sync();
JWT Authentication
bash
// Register
app.post('/api/auth/register', async (req, res) => {
try {
const { name, email, password } = req.body;
// Hash password
const hashedPassword = await bcrypt.hash(password, 10);
// Create user
const user = await User.create({
name,
email,
password: hashedPassword
});
// Generate token
const token = jwt.sign({ userId: user.id }, JWT_SECRET, { expiresIn: '7d' });
res.status(201).json({
message: 'User created successfully',
token,
user: { id: user.id, name: user.name, email: user.email }
});
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// Login
app.post('/api/auth/login', async (req, res) => {
try {
const { email, password } = req.body;
// Find user
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Check password
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Generate token
const token = jwt.sign({ userId: user.id }, JWT_SECRET, { expiresIn: '7d' });
res.json({
message: 'Login successful',
token,
user: { id: user.id, name: user.name, email: user.email }
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Auth middleware
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
try {
const user = await User.findById(decoded.userId);
req.user = user;
next();
} catch (error) {
res.status(500).json({ error: 'Server error' });
}
});
};
// Protected route
app.get('/api/profile', authenticateToken, (req, res) => {
res.json(req.user);
});
Role-based Authorization
javascript
next();
};
};
// Usage
app.get('/api/admin/users', authenticateToken, authorize(['admin']), (req, res) => {
// Admin only route
});
bash
// Storage configuration
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
}
});
// File filter
const fileFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('Invalid file type'), false);
}
};
res.json({
message: 'File uploaded successfully',
filename: req.file.filename,
originalName: req.file.originalname,
size: req.file.size
});
});
// Multiple files
app.post('/api/upload-multiple', upload.array('photos', 5), (req, res) => {
res.json({
message: `${req.files.length} files uploaded`,
files: req.files.map(file => ({
filename: file.filename,
originalName: file.originalname
}))
});
});
Environment Configuration
Using dotenv
bash
.env
NODE_ENV=development
PORT=3000
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_USER=admin
DB_PASS=password
JWT_SECRET=your-jwt-secret-key
API_KEY=your-api-key
config.js
javascript
require('dotenv').config();
const config = {
env: process.env.NODE_ENV || 'development',
port: parseInt(process.env.PORT) || 3000,
database: {
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT) || 5432,
name: process.env.DB_NAME || 'myapp',
user: process.env.DB_USER || 'admin',
password: process.env.DB_PASS || 'password'
},
jwt: {
secret: process.env.JWT_SECRET || 'default-secret',
expiresIn: process.env.JWT_EXPIRES_IN || '7d'
},
apiKey: process.env.API_KEY
};
module.exports = config;
app.js
javascript
app.listen(config.port, () => {
console.log(`Server running on port ${config.port} in ${config.env} mode`);
});
tests/app.test.js
javascript
expect(Array.isArray(response.body)).toBe(true);
});
expect(response.body.name).toBe(userData.name);
expect(response.body.email).toBe(userData.email);
});
test('POST /api/users with invalid data should return 400', async () => {
const response = await request(app)
.post('/api/users')
.send({})
.expect(400);
expect(response.body.error).toBeDefined();
});
});
package.json
json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"jest": {
"testEnvironment": "node"
}
}
Debugging
javascript
// Debug middleware
const debug = require('debug')('app:server');
// Security middleware
app.use(helmet());
app.use(cors());
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/api/', limiter);
// Body parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// Routes
app.use('/api/auth', require('./routes/auth'));
app.use('/api/users', require('./routes/users'));
app.use('/api/posts', require('./routes/posts'));
// Error handling
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something broke!' });
});
// 404 handler
app.use('*', (req, res) => {
res.status(404).json({ error: 'Route not found' });
});
This guide covers the essential Express.js concepts used in production applications. Each section builds
upon the previous ones to create a complete backend application.