0% found this document useful (0 votes)
2 views35 pages

Express.js Backend Development Guide

The document is a comprehensive guide on Express.js backend development, covering essential topics such as setup, routing, middleware, error handling, database integration, authentication, file uploads, and testing. It includes code snippets and explanations for creating a basic server, handling requests and responses, and implementing features like JWT authentication and role-based authorization. Additionally, it addresses database integration with MongoDB and PostgreSQL, as well as file uploads using Multer.

Uploaded by

psyphon10
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views35 pages

Express.js Backend Development Guide

The document is a comprehensive guide on Express.js backend development, covering essential topics such as setup, routing, middleware, error handling, database integration, authentication, file uploads, and testing. It includes code snippets and explanations for creating a basic server, handling requests and responses, and implementing features like JWT authentication and role-based authorization. Additionally, it addresses database integration with MongoDB and PostgreSQL, as well as file uploads using Multer.

Uploaded by

psyphon10
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 35

Express.

js Backend Development Guide


Table of Contents
1. Express.js Setup and Basics
2. Routing and Route Parameters

3. Middleware
4. Request/Response Handling

5. Error Handling
6. Database Integration

7. Authentication & Authorization

8. File Upload and Static Files

9. Environment Configuration

10. Testing and Debugging

Express.js Setup and Basics

Installation

bash

npm init -y
npm install express
npm install -D nodemon

Basic Server
javascript

const express = require('express');


const app = express();
const PORT = process.env.PORT || 3000;

// 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

Routing and Route Parameters

Basic Routes
javascript

const express = require('express');


const app = express();

// HTTP Methods
app.get('/users', (req, res) => {
res.json({ users: [] });
});

app.post('/users', (req, res) => {


const { name, email } = req.body;
res.status(201).json({ id: 1, name, email });
});

app.put('/users/:id', (req, res) => {


const { id } = req.params;
res.json({ message: `Updated user ${id}` });
});

app.delete('/users/:id', (req, res) => {


const { id } = req.params;
res.json({ message: `Deleted user ${id}` });
});

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();

router.get('/', (req, res) => {


res.json({ users: [] });
});

router.get('/:id', (req, res) => {


res.json({ user: { id: req.params.id } });
});

router.post('/', (req, res) => {


res.status(201).json({ message: 'User created' });
});

module.exports = router;

// app.js
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes);

Middleware

Built-in Middleware
javascript

const express = require('express');


const app = express();

// Parse JSON
app.use(express.json({ limit: '10mb' }));

// Parse URL-encoded data


app.use(express.urlencoded({ extended: true }));

// Serve static files


app.use(express.static('public'));

// 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();
};

// Rate limiting middleware


const rateLimit = (maxRequests = 100, windowMs = 15 * 60 * 1000) => {
const requests = new Map();

return (req, res, next) => {


const ip = req.ip;
const now = Date.now();
const windowStart = now - windowMs;

if (!requests.has(ip)) {
requests.set(ip, []);
}

const ipRequests = requests.get(ip);


const recentRequests = ipRequests.filter(time => time > windowStart);

if (recentRequests.length >= maxRequests) {


return res.status(429).json({ error: 'Too many requests' });
}

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);

Error Handling Middleware

javascript

// Error handling middleware (must be last)


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

if (err.type === 'validation') {


return res.status(400).json({ error: err.message });
}

if (err.type === 'unauthorized') {


return res.status(401).json({ error: 'Unauthorized' });
}

res.status(500).json({ error: 'Internal server error' });


});

// 404 handler
app.use('*', (req, res) => {
res.status(404).json({ error: 'Route not found' });
});

Request/Response Handling

Request Object
javascript

app.post('/api/users', (req, res) => {


// Request body
const { name, email } = req.body;

// 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');

// Other useful properties


console.log('Method:', req.method);
console.log('URL:', req.url);
console.log('IP:', req.ip);
console.log('Protocol:', req.protocol);
console.log('Hostname:', req.hostname);
});

Response Object
javascript

app.get('/api/users/:id', (req, res) => {


const user = { id: 1, name: 'John', email: '[email protected]' };

// JSON response
res.json(user);

// Status code with response


res.status(201).json({ message: 'Created' });

// Send different response types


res.send('Plain text');
res.sendFile(path.join(__dirname, 'file.pdf'));

// 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

const validateUser = (req, res, next) => {


const { name, email } = req.body;
const errors = [];

if (!name || name.trim().length < 2) {


errors.push('Name must be at least 2 characters');
}

if (!email || !email.includes('@')) {
errors.push('Valid email is required');
}

if (errors.length > 0) {
return res.status(400).json({ errors });
}

next();
};

app.post('/api/users', validateUser, (req, res) => {


// User data is valid
res.status(201).json({ message: 'User created' });
});

Error Handling

Custom Error Classes


javascript

class AppError extends Error {


constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}

class ValidationError extends AppError {


constructor(message) {
super(message, 400);
}
}

class NotFoundError extends AppError {


constructor(message = 'Resource not found') {
super(message, 404);
}
}

class UnauthorizedError extends AppError {


constructor(message = 'Unauthorized') {
super(message, 401);
}
}

Error Handling Patterns


javascript

// Async error wrapper


const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};

// Usage with async routes


app.get('/api/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) {
throw new NotFoundError('User not found');
}
res.json(user);
}));

// Global error handler


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

// 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

MongoDB with Mongoose

bash

npm install mongoose


javascript
const mongoose = require('mongoose');

// 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 }
});

const User = mongoose.model('User', userSchema);

// 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 });
}
});

app.post('/api/users', async (req, res) => {


try {
const user = new User(req.body);
await user.save();
res.status(201).json(user);
} catch (error) {
res.status(400).json({ error: error.message });
}
});

app.get('/api/users/:id', async (req, res) => {


try {
const user = await User.findById(req.params.id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
} catch (error) {
res.status(500).json({ error: error.message });
}
});

PostgreSQL with Sequelize

bash

npm install sequelize pg pg-hstore


javascript

const { Sequelize, DataTypes } = require('sequelize');

const sequelize = new Sequelize('database', 'username', 'password', {


host: 'localhost',
dialect: 'postgres'
});

// 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();

// CRUD with Sequelize


app.get('/api/users', async (req, res) => {
const users = await User.findAll();
res.json(users);
});

app.post('/api/users', async (req, res) => {


const user = await User.create(req.body);
res.status(201).json(user);
});

Authentication & Authorization

JWT Authentication

bash

npm install jsonwebtoken bcryptjs


javascript
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');

const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';

// 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' });
}

jwt.verify(token, JWT_SECRET, async (err, decoded) => {


if (err) {
return res.status(403).json({ error: 'Invalid token' });
}

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

const authorize = (roles = []) => {


return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Unauthorized' });
}

if (roles.length && !roles.includes(req.user.role)) {


return res.status(403).json({ error: 'Forbidden' });
}

next();
};
};

// Usage
app.get('/api/admin/users', authenticateToken, authorize(['admin']), (req, res) => {
// Admin only route
});

app.delete('/api/users/:id', authenticateToken, authorize(['admin', 'moderator']), (req


// Admin or moderator only
});

File Upload and Static Files

File Upload with Multer

bash

npm install multer


javascript
const multer = require('multer');
const path = require('path');

// 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);
}
};

const upload = multer({


storage,
fileFilter,
limits: { fileSize: 5 * 1024 * 1024 } // 5MB
});

// Single file upload


app.post('/api/upload', upload.single('avatar'), (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'No file uploaded' });
}

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
}))
});
});

// Serve static files


app.use('/uploads', express.static('uploads'));

Environment Configuration

Using dotenv

bash

npm install dotenv

.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
};

// Validate required config


if (!config.jwt.secret || config.jwt.secret === 'default-secret') {
console.error('JWT_SECRET must be set in environment variables');
process.exit(1);
}

module.exports = config;

app.js

javascript

const config = require('./config');

app.listen(config.port, () => {
console.log(`Server running on port ${config.port} in ${config.env} mode`);
});

Testing and Debugging

Testing with Jest and Supertest


bash

npm install -D jest supertest

tests/app.test.js
javascript

const request = require('supertest');


const app = require('../app');

describe('User API', () => {


test('GET /api/users should return users array', async () => {
const response = await request(app)
.get('/api/users')
.expect(200);

expect(Array.isArray(response.body)).toBe(true);
});

test('POST /api/users should create user', async () => {


const userData = {
name: 'John Doe',
email: '[email protected]'
};

const response = await request(app)


.post('/api/users')
.send(userData)
.expect(201);

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');

app.use((req, res, next) => {


debug(`${req.method} ${req.url}`);
next();
});

// VS Code debugging configuration


// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/app.js",
"env": {
"NODE_ENV": "development",
"DEBUG": "app:*"
}
}
]
}
Complete Example Application
app.js
javascript
const express = require('express');
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());

// 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' });
});

const PORT = process.env.PORT || 3000;


app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
module.exports = app;

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.

You might also like