Table Of Contents
Problem
You need to determine if an incoming request is an AJAX call (made with fetch, XMLHttpRequest, or libraries like Axios) to respond with JSON instead of HTML.
Solution
const express = require('express');
const app = express();
// Helper function to check if request is AJAX
function isAjax(req) {
return req.xhr ||
req.headers['x-requested-with'] === 'XMLHttpRequest' ||
req.headers.accept?.includes('application/json') ||
req.headers['content-type']?.includes('application/json');
}
// Route that handles both AJAX and regular requests
app.get('/users', (req, res) => {
const users = [
{ id: 1, name: 'John', email: 'john@example.com' },
{ id: 2, name: 'Jane', email: 'jane@example.com' }
];
if (isAjax(req)) {
// Return JSON for AJAX requests
res.json({
success: true,
data: users,
count: users.length
});
} else {
// Return HTML for regular browser requests
const userList = users.map(user =>
`<li>${user.name} (${user.email})</li>`
).join('');
res.send(`
<h1>Users List</h1>
<ul>${userList}</ul>
<script>
// AJAX example
fetch('/users', {
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
.then(res => res.json())
.then(data => console.log('AJAX response:', data));
</script>
`);
}
});
// Middleware to detect and log AJAX requests
app.use((req, res, next) => {
if (isAjax(req)) {
console.log(`AJAX ${req.method} request to ${req.path}`);
req.isAjax = true;
} else {
console.log(`Regular ${req.method} request to ${req.path}`);
req.isAjax = false;
}
next();
});
// Using the middleware flag
app.post('/submit', (req, res) => {
const result = { message: 'Form submitted successfully' };
if (req.isAjax) {
res.json(result);
} else {
res.redirect('/success?message=' + encodeURIComponent(result.message));
}
});
// API-first approach with explicit content negotiation
app.get('/api/products', (req, res) => {
const products = ['Laptop', 'Phone', 'Tablet'];
const acceptHeader = req.headers.accept || '';
if (acceptHeader.includes('text/html') && !isAjax(req)) {
// Browser request expecting HTML
res.send(`
<h1>Products</h1>
<ul>${products.map(p => `<li>${p}</li>`).join('')}</ul>
`);
} else {
// AJAX or API client expecting JSON
res.json({
products,
total: products.length,
requestType: 'API'
});
}
});
// Error handling with different responses
app.use((err, req, res, next) => {
console.error(err.stack);
if (isAjax(req)) {
res.status(500).json({
error: 'Internal server error',
message: err.message
});
} else {
res.status(500).send(`
<h1>Error</h1>
<p>Something went wrong: ${err.message}</p>
`);
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Test AJAX detection:
# Regular request
curl http://localhost:3000/users
# AJAX request
curl -H "X-Requested-With: XMLHttpRequest" http://localhost:3000/users
# JSON request
curl -H "Accept: application/json" http://localhost:3000/users
Explanation
req.xhr
is Express's built-in property that checks for X-Requested-With: XMLHttpRequest
header. Modern libraries and fetch() don't always send this header, so checking Accept
headers for JSON content is more reliable.
The isAjax()
function combines multiple detection methods for better compatibility. This pattern allows you to build progressive web applications that work with or without JavaScript, providing appropriate responses for each request type.
Share this article
Add Comment
No comments yet. Be the first to comment!