Navigation

Node.js

How to Check if Request is AJAX in Express

Detect AJAX requests in Express.js by checking headers. Handle API calls differently from regular page requests using X-Requested-With header detection.

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!

More from Node.js