Express Content Types
When building web applications with Express.js, understanding how to handle different types of content is essential. In HTTP communication, the Content-Type
header specifies the format of the data being sent in a request or response. This guide will help you understand how to work with various content types in Express.js applications.
Introduction to Content Types
In web development, content types (also known as MIME types) tell the client or server what kind of data is being transmitted. Express.js provides built-in middleware and methods to handle different content types efficiently.
Some common content types include:
application/json
- JSON dataapplication/x-www-form-urlencoded
- Form datamultipart/form-data
- Form data with file uploadstext/plain
- Plain texttext/html
- HTML content
Built-in Middleware for Content Parsing
Express.js provides middleware functions that automatically parse incoming request bodies in different formats:
1. Parsing JSON Data
To handle JSON data in requests, use the express.json()
middleware:
const express = require('express');
const app = express();
// Add middleware to parse JSON requests
app.use(express.json());
app.post('/api/users', (req, res) => {
// req.body now contains the parsed JSON data
console.log(req.body);
res.status(201).json({ message: 'User created', user: req.body });
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Example JSON request:
POST /api/users HTTP/1.1
Host: localhost:3000
Content-Type: application/json
{
"name": "John Doe",
"email": "[email protected]",
"age": 30
}
2. Parsing URL-encoded Forms
For traditional form submissions (without file uploads), use express.urlencoded()
:
// Add middleware to parse URL-encoded requests
app.use(express.urlencoded({ extended: true }));
app.post('/login', (req, res) => {
const { username, password } = req.body;
console.log(`Login attempt: ${username}`);
// Process login logic here
res.send(`Welcome, ${username}!`);
});
The extended: true
option allows parsing rich objects and arrays in the URL-encoded format using the qs library.
Example form submission:
<form action="/login" method="POST">
<input type="text" name="username">
<input type="password" name="password">
<button type="submit">Login</button>
</form>
3. Handling File Uploads with Multer
For handling multipart/form-data
(forms with file uploads), Express doesn't have built-in middleware. Instead, you can use the popular multer
package:
const express = require('express');
const multer = require('multer');
const app = express();
// Configure storage
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname);
}
});
const upload = multer({ storage: storage });
// Single file upload
app.post('/upload', upload.single('profileImage'), (req, res) => {
if (!req.file) {
return res.status(400).send('No file uploaded');
}
res.send({
message: 'File uploaded successfully',
file: req.file
});
});
// Multiple files upload
app.post('/upload-multiple', upload.array('images', 5), (req, res) => {
res.send({
message: 'Files uploaded successfully',
files: req.files
});
});
Example HTML form for file upload:
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="profileImage">
<button type="submit">Upload</button>
</form>
Setting Content Types in Responses
Just as you need to handle content types in requests, it's equally important to set the correct content type in responses:
// JSON response
app.get('/api/data', (req, res) => {
const data = {
name: "Express App",
version: "1.0.0"
};
// Express automatically sets Content-Type: application/json
res.json(data);
});
// HTML response
app.get('/html-page', (req, res) => {
res.set('Content-Type', 'text/html');
res.send('<html><body><h1>Hello World</h1></body></html>');
});
// Plain text response
app.get('/text', (req, res) => {
res.set('Content-Type', 'text/plain');
res.send('This is plain text content');
});
// PDF response
app.get('/download-pdf', (req, res) => {
// Assuming `report.pdf` exists in the server
res.set({
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="report.pdf"'
});
res.sendFile('/path/to/report.pdf');
});
Content Negotiation
Content negotiation allows a client to specify which content type they prefer to receive. Express can use the Accept
header to determine the appropriate response format:
app.get('/api/users/:id', (req, res) => {
const userId = req.params.id;
const user = { id: userId, name: 'Alice', email: '[email protected]' };
// Check what format the client wants to receive
res.format({
'application/json': () => {
res.json(user);
},
'text/html': () => {
res.send(`
<html>
<body>
<h1>User Details</h1>
<p>ID: ${user.id}</p>
<p>Name: ${user.name}</p>
<p>Email: ${user.email}</p>
</body>
</html>
`);
},
'text/plain': () => {
res.send(`User ${user.id}: ${user.name} (${user.email})`);
},
'default': () => {
// If no formats match, send as JSON
res.status(406).json({ error: 'Not Acceptable' });
}
});
});
Real-World Example: REST API with Multiple Content Types
Let's create a more comprehensive example: a product API that can handle different content types for both requests and responses:
const express = require('express');
const multer = require('multer');
const path = require('path');
const app = express();
// Set up middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Configure multer for file uploads
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'product-images/');
},
filename: (req, file, cb) => {
cb(null, `product-${Date.now()}${path.extname(file.originalname)}`);
}
});
const upload = multer({ storage });
// In-memory database
const products = [];
// Create a product (supports JSON and form submissions)
app.post('/api/products', (req, res) => {
const product = {
id: Date.now().toString(),
name: req.body.name,
price: parseFloat(req.body.price),
description: req.body.description,
createdAt: new Date()
};
products.push(product);
res.status(201).json(product);
});
// Add product image
app.post('/api/products/:id/image', upload.single('productImage'), (req, res) => {
const productId = req.params.id;
const product = products.find(p => p.id === productId);
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
if (!req.file) {
return res.status(400).json({ error: 'No image uploaded' });
}
product.imageUrl = `/images/${req.file.filename}`;
res.json(product);
});
// Get products with content negotiation
app.get('/api/products', (req, res) => {
res.format({
'application/json': () => {
res.json(products);
},
'text/html': () => {
let html = '<html><body><h1>Products</h1><ul>';
products.forEach(product => {
html += `<li>${product.name}: $${product.price}</li>`;
});
html += '</ul></body></html>';
res.send(html);
},
'text/csv': () => {
let csv = 'ID,Name,Price,Description\n';
products.forEach(product => {
csv += `${product.id},${product.name},${product.price},"${product.description}"\n`;
});
res.set('Content-Type', 'text/csv');
res.send(csv);
},
'default': () => {
res.status(406).json({ error: 'Not Acceptable' });
}
});
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Security Considerations
When handling different content types, keep these security best practices in mind:
- Always set content size limits to prevent denial-of-service attacks:
// Limit JSON payloads to 1MB
app.use(express.json({ limit: '1mb' }));
// Limit URL-encoded payloads to 1MB
app.use(express.urlencoded({ extended: true, limit: '1mb' }));
- Validate content types to prevent unexpected behavior:
app.post('/api/data', (req, res, next) => {
const contentType = req.headers['content-type'];
if (!contentType || contentType.indexOf('application/json') !== 0) {
return res.status(415).json({ error: 'Unsupported Media Type' });
}
next();
});
- Sanitize inputs to prevent injection attacks when rendering content.
Summary
In this guide, you've learned:
- How to parse different request body formats with Express middleware
- How to handle file uploads using multer
- How to set appropriate content types for responses
- How to implement content negotiation for different response formats
- Security considerations when working with different content types
Understanding how to work with various content types is essential for building robust Express.js applications that can communicate effectively with different clients and services.
Additional Resources
Exercises
- Create an Express application that accepts a text file upload and counts the number of words in it.
- Implement an API endpoint that can return user data in either JSON or XML format based on the Accept header.
- Create a form submission handler that validates different types of data (numbers, emails, dates) and returns appropriate error messages.
- Build a simple file conversion service that accepts an image and can convert it to different formats.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)