🎯 Learning Objectives
After completing this course, you will be able to:
- Understand server-side programming concepts
- Build RESTful APIs with Node.js/Express
- Work with databases (SQL and NoSQL)
- Implement authentication and authorization
- Handle file uploads and processing
- Implement error handling and logging
- Deploy backend applications
⚡ Prerequisites
- Strong JavaScript knowledge
- Understanding of HTTP protocol
- Basic command line skills
- Node.js and npm installed
- Database basics (SQL/NoSQL)
1. Introduction to Backend Development
What is Backend Development?
Backend development involves server-side logic, databases, and APIs that power web applications.
- Server: Handles requests and responses
- Database: Stores and retrieves data
- API: Interface for frontend communication
- Business Logic: Core application functionality
Backend Responsibilities
- Data processing and validation
- User authentication and authorization
- Database operations (CRUD)
- API endpoint creation
- File handling and storage
- Email and notifications
- Security implementation
2. Node.js & Express
What is Node.js?
Node.js is a JavaScript runtime built on Chrome's V8 engine that allows you to run JavaScript on the server.
Basic Express Server
// server.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Routes
app.get('/', (req, res) => {
res.json({ message: 'Welcome to the API' });
});
app.get('/api/users', (req, res) => {
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
res.json(users);
});
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
const newUser = {
id: Date.now(),
name,
email
};
res.status(201).json(newUser);
});
// Start server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Express Router
// routes/users.js
const express = require('express');
const router = express.Router();
// GET all users
router.get('/', async (req, res) => {
try {
const users = await User.find();
res.json(users);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// GET user by ID
router.get('/: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 });
}
});
// POST create user
router.post('/', 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 });
}
});
// PUT update user
router.put('/:id', async (req, res) => {
try {
const user = await User.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true, runValidators: true }
);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// DELETE user
router.delete('/:id', async (req, res) => {
try {
const user = await User.findByIdAndDelete(req.params.id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json({ message: 'User deleted successfully' });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
module.exports = router;
// In server.js
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes);
3. Database Integration
MongoDB with Mongoose
// Install: npm install mongoose
const mongoose = require('mongoose');
// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/myapp', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('MongoDB connected'))
.catch(err => console.error('MongoDB connection error:', err));
// Define Schema
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Name is required'],
trim: true,
minlength: 2,
maxlength: 50
},
email: {
type: String,
required: [true, 'Email is required'],
unique: true,
lowercase: true,
match: [/^\S+@\S+\.\S+$/, 'Please enter a valid email']
},
password: {
type: String,
required: [true, 'Password is required'],
minlength: 6
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user'
},
createdAt: {
type: Date,
default: Date.now
}
});
// Create Model
const User = mongoose.model('User', userSchema);
// CRUD Operations
// Create
const newUser = new User({
name: 'John Doe',
email: 'john@example.com',
password: 'hashedpassword'
});
await newUser.save();
// Read
const users = await User.find();
const user = await User.findById(userId);
const userByEmail = await User.findOne({ email: 'john@example.com' });
// Update
await User.findByIdAndUpdate(userId, { name: 'Jane Doe' });
await User.updateOne({ email: 'john@example.com' }, { role: 'admin' });
// Delete
await User.findByIdAndDelete(userId);
await User.deleteOne({ email: 'john@example.com' });
PostgreSQL with Sequelize
// Install: npm install sequelize pg pg-hstore
const { Sequelize, DataTypes } = require('sequelize');
// Connect to PostgreSQL
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'postgres'
});
// Test connection
sequelize.authenticate()
.then(() => console.log('PostgreSQL connected'))
.catch(err => console.error('Connection error:', err));
// Define Model
const User = sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: false,
validate: {
len: [2, 50]
}
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
},
role: {
type: DataTypes.ENUM('user', 'admin'),
defaultValue: 'user'
}
});
// Sync database
await sequelize.sync();
// CRUD Operations
// Create
const user = await User.create({
name: 'John Doe',
email: 'john@example.com',
password: 'hashedpassword'
});
// Read
const users = await User.findAll();
const user = await User.findByPk(userId);
const userByEmail = await User.findOne({ where: { email: 'john@example.com' } });
// Update
await User.update(
{ name: 'Jane Doe' },
{ where: { id: userId } }
);
// Delete
await User.destroy({ where: { id: userId } });
4. Authentication & Authorization
JWT Authentication
// Install: npm install bcryptjs jsonwebtoken
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
// Register User
app.post('/api/auth/register', async (req, res) => {
try {
const { name, email, password } = req.body;
// Check if user exists
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).json({ error: 'User already exists' });
}
// Hash password
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
// Create user
const user = new User({
name,
email,
password: hashedPassword
});
await user.save();
// Generate token
const token = jwt.sign(
{ userId: user._id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.status(201).json({
message: 'User registered successfully',
token,
user: {
id: user._id,
name: user.name,
email: user.email
}
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Login User
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 isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Generate token
const token = jwt.sign(
{ userId: user._id, email: user.email },
process.env.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 });
}
});
// Authentication Middleware
const authenticate = async (req, res, next) => {
try {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findById(decoded.userId);
if (!user) {
return res.status(401).json({ error: 'User not found' });
}
req.user = user;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
};
// Protected Route
app.get('/api/profile', authenticate, (req, res) => {
res.json({
user: {
id: req.user._id,
name: req.user.name,
email: req.user.email
}
});
});
5. Middleware
Custom Middleware
// Logging Middleware
const logger = (req, res, next) => {
console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
next();
};
// Error Handling Middleware
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: {
message: err.message || 'Internal Server Error',
status: err.status || 500
}
});
};
// Validation Middleware
const validateUser = (req, res, next) => {
const { name, email, password } = req.body;
if (!name || !email || !password) {
return res.status(400).json({
error: 'Name, email, and password are required'
});
}
if (password.length < 6) {
return res.status(400).json({
error: 'Password must be at least 6 characters'
});
}
next();
};
// Rate Limiting Middleware
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests, please try again later'
});
// CORS Middleware
const cors = require('cors');
app.use(cors({
origin: 'http://localhost:3000',
credentials: true
}));
// Apply Middleware
app.use(logger);
app.use('/api/', limiter);
app.post('/api/users', validateUser, createUser);
app.use(errorHandler);
6. Security Best Practices
🔒 Security Essentials
- Input Validation: Always validate and sanitize user input
- SQL Injection: Use parameterized queries or ORMs
- XSS Protection: Escape output and use Content Security Policy
- CSRF Protection: Implement CSRF tokens
- Rate Limiting: Prevent brute force attacks
- HTTPS: Always use SSL/TLS in production
- Environment Variables: Never commit secrets to version control
- Dependencies: Keep packages updated and audit regularly
Security Implementation
// Install security packages
// npm install helmet express-mongo-sanitize xss-clean express-rate-limit
const helmet = require('helmet');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');
const rateLimit = require('express-rate-limit');
// Helmet - Set security HTTP headers
app.use(helmet());
// Data sanitization against NoSQL query injection
app.use(mongoSanitize());
// Data sanitization against XSS
app.use(xss());
// Rate limiting
const limiter = rateLimit({
max: 100,
windowMs: 60 * 60 * 1000,
message: 'Too many requests from this IP'
});
app.use('/api', limiter);
// Environment variables
require('dotenv').config();
const config = {
port: process.env.PORT || 3000,
dbUrl: process.env.DATABASE_URL,
jwtSecret: process.env.JWT_SECRET,
nodeEnv: process.env.NODE_ENV
};
7. Deployment
Deployment Checklist
// package.json scripts
{
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"test": "jest",
"build": "npm run test"
}
}
// .env.example (commit this, not .env)
PORT=3000
DATABASE_URL=your_database_url
JWT_SECRET=your_jwt_secret
NODE_ENV=production
// .gitignore
node_modules/
.env
*.log
.DS_Store
// Procfile (for Heroku)
web: node server.js
// Docker
FROM node:16
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Practice Exercises
Create a RESTful API for a blog with:
- User registration and authentication
- CRUD operations for blog posts
- Comments on posts
- Authorization (only author can edit/delete)
Implement file upload functionality:
- Upload images with validation
- Store files in cloud storage (AWS S3 or similar)
- Generate thumbnails
- Return file URLs
Build a real-time chat system:
- WebSocket connection with Socket.io
- User authentication
- Message persistence in database
- Online user status
🎉 Summary
You've completed the Backend Development course! You now know:
- Server-side programming with Node.js and Express
- Database integration (MongoDB and PostgreSQL)
- Authentication and authorization with JWT
- Middleware implementation and usage
- Security best practices
- API development and deployment
- Error handling and logging
Total points available: 10/10