HTML & JS Topic
Node.js Express.js Fundamentals
What is Express.js?
Express.js is a minimal, flexible, and unopinionated web application framework for Node.js. It's the most popular Node.js framework that provides a robust set of features for building web applications and APIs.
Key Characteristics:
-
Minimal and unopinionated: Doesn't impose strict patterns
-
Middleware-based: Uses middleware functions for request processing
-
Routing: Powerful routing system
-
HTTP utilities: Methods and middleware for HTTP requests
-
View system: Template engine integration
-
Fast: Built on top of Node.js http module
Why Express.js over plain http module?
javascript
// Plain Node.js http module
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url === '/' && req.method === 'GET') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Home Page');
} else if (req.url === '/api/users' && req.method === 'GET') {
// Complex routing logic needed...
}
});
// Express.js (much cleaner)
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Home Page');
});
app.get('/api/users', (req, res) => {
// Clean routing
});
Installation:
bash
# Create new project
mkdir express-app
cd express-app
npm init -y
# Install Express
npm install express
# For development (optional)
npm install -D nodemon
Creating an Express Server
Basic Express Server:
javascript
// app.js
const express = require('express');
// Create Express application
const app = express();
const PORT = process.env.PORT || 3000;
// Define routes
app.get('/', (req, res) => {
res.send('Hello Express!');
});
// Start server
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
Advanced Server Setup:
javascript
// server.js
const express = require('express');
const path = require('path');
const app = express();
// Configuration
app.set('port', process.env.PORT || 3000);
app.set('view engine', 'ejs'); // If using template engine
app.set('views', path.join(__dirname, 'views'));
// Parse JSON and URL-encoded bodies
app.use(express.json()); // For parsing application/json
app.use(express.urlencoded({ extended: true })); // For parsing application/x-www-form-urlencoded
// Static files middleware
app.use(express.static(path.join(__dirname, 'public')));
// Custom middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next(); // Pass control to next middleware
});
// Routes
app.get('/', (req, res) => {
res.send('Welcome to Express Server');
});
// Error handling middleware (should be last)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
// 404 handler (catch all)
app.use((req, res) => {
res.status(404).send('Page not found');
});
// Start server with error handling
const server = app.listen(app.get('port'), () => {
console.log(`Server listening on port ${app.get('port')}`);
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM signal received: closing HTTP server');
server.close(() => {
console.log('HTTP server closed');
});
});
Package.json Scripts:
json
{
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"test": "jest"
}
}
Routing (GET, POST, PUT, DELETE)
Basic Routing Methods:
javascript
const express = require('express');
const app = express();
// GET method - Retrieve data
app.get('/users', (req, res) => {
res.send('GET: List all users');
});
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`GET: User with ID ${userId}`);
});
// POST method - Create data
app.post('/users', (req, res) => {
res.send('POST: Create new user');
});
// PUT method - Update entire resource
app.put('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`PUT: Update user ${userId}`);
});
// PATCH method - Partial update
app.patch('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`PATCH: Partially update user ${userId}`);
});
// DELETE method - Delete data
app.delete('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`DELETE: Delete user ${userId}`);
});
// All HTTP methods
app.all('/secret', (req, res, next) => {
console.log('Accessing the secret section...');
next(); // Pass control to the next handler
});
// Chained route handlers
app.route('/book')
.get((req, res) => {
res.send('Get a random book');
})
.post((req, res) => {
res.send('Add a book');
})
.put((req, res) => {
res.send('Update the book');
});
Route Parameters:
javascript
// Single parameter
app.get('/users/:userId', (req, res) => {
res.send(`User ID: ${req.params.userId}`);
});
// Multiple parameters
app.get('/users/:userId/books/:bookId', (req, res) => {
res.send(`User ${req.params.userId}, Book ${req.params.bookId}`);
});
// Optional parameters
app.get('/flights/:from?/:to?', (req, res) => {
const from = req.params.from || 'JFK';
const to = req.params.to || 'LAX';
res.send(`Searching flights from ${from} to ${to}`);
});
// Regex in route parameters
app.get('/user/:id(\\d+)', (req, res) => {
// Only matches if id is digits
res.send(`User ID (digits only): ${req.params.id}`);
});
app.get('/file/:name.:ext', (req, res) => {
// Matches /file/photo.jpg
res.send(`File: ${req.params.name}.${req.params.ext}`);
});
Query Strings:
javascript
// GET /search?q=express&sort=desc
app.get('/search', (req, res) => {
const query = req.query.q;
const sort = req.query.sort || 'asc';
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
res.json({
message: 'Search results',
query,
sort,
page,
limit,
results: []
});
});
Route Handlers (Multiple Callbacks):
javascript
// Multiple callback functions
app.get('/example',
(req, res, next) => {
console.log('First callback');
next(); // Pass to next callback
},
(req, res, next) => {
console.log('Second callback');
next();
},
(req, res) => {
console.log('Third callback');
res.send('Response sent');
}
);
// Array of callback functions
const cb1 = (req, res, next) => {
console.log('Callback 1');
next();
};
const cb2 = (req, res, next) => {
console.log('Callback 2');
next();
};
const cb3 = (req, res) => {
res.send('Response from callback chain');
};
app.get('/chain', [cb1, cb2, cb3]);
Router Module (Modular Routes):
javascript
// routes/users.js
const express = require('express');
const router = express.Router();
// Middleware specific to this router
router.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
// Define routes
router.get('/', (req, res) => {
res.send('Users home page');
});
router.get('/:id', (req, res) => {
res.send(`User ${req.params.id}`);
});
router.post('/', (req, res) => {
res.send('Create user');
});
module.exports = router;
// In main app.js
const usersRouter = require('./routes/users');
app.use('/users', usersRouter); // All routes in usersRouter are prefixed with /users
Request & Response Objects
Request Object (req):
Contains information about the HTTP request.
javascript
app.use((req, res, next) => {
// Request properties
console.log('Request Properties:');
console.log('Method:', req.method); // GET, POST, etc.
console.log('URL:', req.url); // /path?query=string
console.log('Path:', req.path); // /path
console.log('Hostname:', req.hostname); // localhost
console.log('IP:', req.ip); // ::1
console.log('Protocol:', req.protocol); // http
console.log('Secure:', req.secure); // false (true for HTTPS)
console.log('Query:', req.query); // Parsed query string object
console.log('Params:', req.params); // Route parameters
console.log('Headers:', req.headers); // Request headers
console.log('Cookies:', req.cookies); // Requires cookie-parser middleware
console.log('Body:', req.body); // Requires body-parser middleware
console.log('Files:', req.files); // Requires multer middleware
// Request methods
console.log('Is AJAX?', req.xhr); // true for XMLHttpRequest
console.log('Accepts JSON?', req.accepts('json'));
console.log('Accepts HTML?', req.accepts('html'));
next();
});
Accessing Request Data:
javascript
// Route with multiple ways to access data
app.post('/api/users/:id', (req, res) => {
// Route parameters
const userId = req.params.id;
// Query string
const debug = req.query.debug === 'true';
// Request body (JSON or form data)
const { name, email, age } = req.body;
// Headers
const contentType = req.get('Content-Type');
const authorization = req.header('Authorization');
// Check content type
if (req.is('json')) {
console.log('Request is JSON');
}
res.json({
userId,
name,
email,
age,
debug,
contentType
});
});
Response Object (res):
Used to send response to client.
javascript
// Sending different types of responses
app.get('/responses', (req, res) => {
// Send plain text
// res.send('Hello World');
// Send JSON
// res.json({ message: 'Success', data: { id: 1 } });
// Send file
// res.sendFile('/path/to/file.pdf');
// Send status code only
// res.sendStatus(204);
// Set status and send
// res.status(201).send('Resource created');
// Set headers
res.set('Content-Type', 'text/plain');
res.set('X-Custom-Header', 'CustomValue');
// Set multiple headers
res.set({
'Content-Type': 'application/json',
'X-Powered-By': 'Express'
});
// Set cookie (requires cookie-parser)
// res.cookie('username', 'john', { maxAge: 900000, httpOnly: true });
// Clear cookie
// res.clearCookie('username');
// Redirect
// res.redirect('/new-location');
// res.redirect(301, '/permanent-location');
// res.redirect('https://google.com');
// Render template (with view engine)
// res.render('index', { title: 'Home', users: [] });
// End response (no data)
// res.end();
// Send response
res.send('Response sent');
});
// Chaining response methods
app.get('/chain', (req, res) => {
res
.status(200)
.set('X-Custom', 'Value')
.cookie('token', 'abc123', { httpOnly: true })
.json({ success: true, message: 'Chained response' });
});
Response Methods in Detail:
javascript
// 1. res.send() - Auto-detects content type
app.get('/send', (req, res) => {
res.send('String'); // text/html
res.send('<p>HTML</p>'); // text/html
res.send({ message: 'JSON' }); // application/json
res.send(Buffer.from('buffer')); // application/octet-stream
});
// 2. res.json() - Always sends JSON
app.get('/json', (req, res) => {
res.json({ success: true, data: null });
// Equivalent to: res.send({ success: true })
});
// 3. res.jsonp() - JSON with padding (for JSONP)
app.get('/jsonp', (req, res) => {
res.jsonp({ user: 'tobi' });
// If ?callback=cb, returns: cb({ "user": "tobi" })
});
// 4. res.download() - Prompt file download
app.get('/download', (req, res) => {
res.download('/path/to/file.pdf');
res.download('/path/to/file.pdf', 'custom-name.pdf');
res.download('/path/to/file.pdf', 'custom-name.pdf', (err) => {
if (err) {
// Handle error
}
});
});
// 5. res.sendFile() - Send file
app.get('/file', (req, res) => {
const options = {
root: path.join(__dirname, 'public'),
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
};
res.sendFile('image.jpg', options, (err) => {
if (err) {
next(err);
}
});
});
// 6. res.render() - Render view template
app.get('/view', (req, res) => {
res.render('index', {
title: 'Home Page',
users: [{ name: 'John' }, { name: 'Jane' }]
});
});
Middleware Concept
What is Middleware?
Middleware functions are functions that have access to:
-
Request object (req)
-
Response object (res)
-
Next middleware function (next)
javascript
// Middleware function signature
function middleware(req, res, next) {
// Do something with req/res
// Call next() to pass to next middleware
// Or send response to end chain
next();
}
Types of Middleware:
javascript
// 1. Application-level middleware
app.use((req, res, next) => {
console.log('Application middleware - runs for ALL routes');
next();
});
// 2. Router-level middleware
const router = express.Router();
router.use((req, res, next) => {
console.log('Router middleware');
next();
});
// 3. Built-in middleware
app.use(express.json()); // Parse JSON bodies
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded bodies
app.use(express.static('public')); // Serve static files
// 4. Third-party middleware
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
app.use(cors());
app.use(helmet());
app.use(morgan('combined'));
// 5. Error-handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
Creating Custom Middleware:
javascript
// Logger 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 (simplified)
if (token !== 'secret-token') {
return res.status(403).json({ error: 'Invalid token' });
}
// Attach user to request
req.user = { id: 1, name: 'John Doe' };
next();
};
// 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();
};
// Using middleware
app.use(logger); // Applies to all routes
app.get('/public', (req, res) => {
res.send('Public route');
});
app.get('/private', authenticate, (req, res) => {
res.json({ message: 'Private route', user: req.user });
});
app.post('/register', validateUser, (req, res) => {
// If we get here, validation passed
res.status(201).json({ message: 'User registered successfully' });
});
Middleware Execution Order:
javascript
// Order matters in middleware!
app.use((req, res, next) => {
console.log('First middleware');
req.customData = 'Data from first middleware';
next();
});
app.use((req, res, next) => {
console.log('Second middleware');
console.log('Custom data:', req.customData);
next();
});
app.use('/admin', (req, res, next) => {
console.log('Admin middleware - only for /admin routes');
next();
});
app.get('/', (req, res) => {
res.send('Home - all middleware ran');
});
app.get('/admin', (req, res) => {
res.send('Admin page - admin middleware ran');
});
Error Handling Middleware:
javascript
// Must have 4 parameters: (err, req, res, next)
const errorHandler = (err, req, res, next) => {
console.error('Error:', err);
// Set default status code
const statusCode = err.statusCode || 500;
const message = err.message || 'Internal Server Error';
// Send error response
res.status(statusCode).json({
success: false,
error: {
message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
}
});
};
// Async error handler wrapper
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Usage with async/await
app.get('/async-route', asyncHandler(async (req, res) => {
const data = await someAsyncOperation();
if (!data) {
throw new Error('Data not found');
}
res.json(data);
}));
// Register error handler (should be last middleware)
app.use(errorHandler);
Complete Express Application Example:
javascript
// app.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const app = express();
// Middleware stack
app.use(helmet()); // Security headers
app.use(cors()); // CORS enabled
app.use(morgan('dev')); // HTTP request logging
app.use(express.json()); // Parse JSON bodies
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded bodies
// Custom middleware
app.use((req, res, next) => {
req.requestTime = new Date().toISOString();
next();
});
// Routes
app.get('/', (req, res) => {
res.json({
message: 'Welcome to Express API',
timestamp: req.requestTime,
endpoints: [
'GET /api/users',
'POST /api/users',
'GET /api/users/:id'
]
});
});
// API routes
app.get('/api/users', (req, res) => {
// Simulate database query
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
res.json(users);
});
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: 'Name and email are required' });
}
// Simulate creating user
const newUser = {
id: Date.now(),
name,
email
};
res.status(201).json({
message: 'User created',
user: newUser
});
});
// 404 handler
app.use((req, res) => {
res.status(404).json({ error: 'Route not found' });
});
// Error handler (must be last)
app.use((err, req, res, next) => {
console.error(err.stack);
const status = err.status || 500;
const message = err.message || 'Internal Server Error';
res.status(status).json({
error: {
message,
status
}
});
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
module.exports = app; // For testing
Testing the Express Server:
bash
# Start server
npm run dev
# Test endpoints
curl http://localhost:3000/
curl http://localhost:3000/api/users
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"John","email":"john@example.com"}'
# Test error
curl http://localhost:3000/nonexistent
Express.js simplifies Node.js web development by providing a clean, middleware-based architecture that makes it easy to handle routes, requests, and responses while maintaining flexibility and performance.
Comments (0)
No comments yet. Be the first to comment!