Introduction: The Internet's Postal Service
Imagine the internet as a vast postal service, where millions of letters (requests) are being sent back and forth between clients (the senders) and servers (the recipients). In this analogy, Express.js acts as our intelligent mail sorting system, helping us organize and handle these messages efficiently. The request (req) and response (res) objects are like the standardized envelopes and packages that make this communication possible.
The Request Object (req): Opening the Envelope
Think of the request object as a carefully organized envelope that arrives at your server. Just like how a physical envelope might contain different sections for the address, return address, and contents, the req object has different properties that help us understand what the client wants.
req.body: The Letter Inside
Let's build a simple user registration system to understand req.body:
// First, we need to tell Express to expect JSON data
const express = require('express');
const app = express();
app.use(express.json());
// Our registration endpoint
app.post('/register', (req, res) => {
// req.body contains the user's registration data
const { username, email, password } = req.body;
console.log(`New user registration:
Username: ${username}
Email: ${email}
Password: [hidden]`);
// We'll handle the response later
});
In the real world, this is similar to how a bank processes a new account application form. The teller (server) needs to read all the information filled in the form (req.body) before proceeding with the account creation.
req.query: The Special Instructions
Think of query parameters as special instructions written on the outside of the envelope. Let's build a product search feature:
// Product search endpoint
app.get('/products', (req, res) => {
const { category, maxPrice, sort } = req.query;
console.log(`Searching for products:
Category: ${category || 'all'}
Max Price: ${maxPrice || 'unlimited'}
Sort By: ${sort || 'default'}`);
// Implementation details would go here
});
// Example URL: /products?category=electronics&maxPrice=500&sort=price-asc
This is similar to how you might filter products on an e-commerce website. Each filter option becomes a query parameter in your URL.
req.params: The Addressed Recipient
Route parameters are like specific addresses on an envelope. Let's create a user profile system:
// User profile endpoint
app.get('/users/:userId/profile/:section', (req, res) => {
const { userId, section } = req.params;
console.log(`Accessing user ${userId}'s ${section} section`);
// Implementation details would go here
});
// Example URL: /users/12345/profile/settings
The Response Object (res): Crafting the Reply
If req is the incoming envelope, res is our opportunity to send back our own carefully crafted response. Let's build a complete API endpoint that showcases different response scenarios:
app.post('/orders', async (req, res) => {
try {
const { items, shippingAddress } = req.body;
// Validate the order
if (!items || items.length === 0) {
return res.status(400).json({
error: 'Order must contain at least one item'
});
}
// Process the order (simplified)
const order = {
id: 'ORD' + Date.now(),
items,
shippingAddress,
status: 'confirmed'
};
// Send success response
res.status(201).json({
message: 'Order created successfully',
orderId: order.id,
estimatedDelivery: '2-3 business days'
});
} catch (error) {
// Handle unexpected errors
res.status(500).json({
error: 'Something went wrong',
message: error.message
});
}
});
This code demonstrates various response patterns you'll use in real-world applications:
- Input validation with appropriate error responses
- Successful creation with a 201 status code
- Error handling with 500 status codes
- Structured JSON responses for both success and error cases
Real-World Application: Building a Mini Social Media API
Let's put everything together in a practical example - a simple social media post system:
const express = require('express');
const app = express();
app.use(express.json());
// In-memory storage (replace with a database in production)
const posts = [];
// Create a new post
app.post('/posts', (req, res) => {
const { title, content } = req.body;
// Validate input
if (!title || !content) {
return res.status(400).json({
error: 'Title and content are required'
});
}
// Create post object
const post = {
id: Date.now().toString(),
title,
content,
likes: 0,
comments: [],
createdAt: new Date()
};
posts.push(post);
res.status(201).json(post);
});
// Get posts with filtering and pagination
app.get('/posts', (req, res) => {
const { page = 1, limit = 10, sort = 'newest' } = req.query;
let filteredPosts = [...posts];
// Apply sorting
if (sort === 'newest') {
filteredPosts.sort((a, b) => b.createdAt - a.createdAt);
} else if (sort === 'popular') {
filteredPosts.sort((a, b) => b.likes - a.likes);
}
// Apply pagination
const startIndex = (page - 1) * limit;
const paginatedPosts = filteredPosts.slice(startIndex, startIndex + parseInt(limit));
res.json({
posts: paginatedPosts,
currentPage: parseInt(page),
totalPosts: posts.length
});
});
// Add a comment to a post
app.post('/posts/:postId/comments', (req, res) => {
const { postId } = req.params;
const { text } = req.body;
const post = posts.find(p => p.id === postId);
if (!post) {
return res.status(404).json({ error: 'Post not found' });
}
const comment = {
id: Date.now().toString(),
text,
createdAt: new Date()
};
post.comments.push(comment);
res.status(201).json(comment);
});
// Like a post
app.post('/posts/:postId/like', (req, res) => {
const { postId } = req.params;
const post = posts.find(p => p.id === postId);
if (!post) {
return res.status(404).json({ error: 'Post not found' });
}
post.likes += 1;
res.json({ likes: post.likes });
});
Further Topics to Explore
- Middleware functions in Express
- Error handling middleware
- Request validation techniques
- Response compression
- CORS handling
- File uploads with Express
- Authentication and authorization
- Rate limiting
Best Practices and Tips
When working with req and res objects, keep these guidelines in mind:
- Always validate input data before processing
- Set appropriate status codes for different scenarios
- Structure your JSON responses consistently
- Handle errors gracefully
- Use appropriate HTTP methods for different operations
- Implement proper security headers