Native JavaScript Alternative to jQuery ajax

The modern, native JavaScript alternative to jQuery's $.ajax() is the Fetch API, represented by the global fetch() function. This article outlines the fundamentals of replacing $.ajax() with native JavaScript, demonstrating how to perform basic GET and POST requests, utilize async/await syntax, and handle HTTP errors without relying on third-party libraries.

The Modern Standard: The Fetch API

Introduced to modernize asynchronous HTTP requests, the Fetch API is built directly into all modern web browsers. Unlike $.ajax(), which relies on callbacks or jQuery's deferred objects, fetch() is natively Promise-based, making it cleaner to read and easier to integrate with modern JavaScript features like async/await.

GET Request: jQuery vs. Fetch

In jQuery, a simple GET request typically looks like this:

$.ajax({
  url: 'https://api.example.com/data',
  type: 'GET',
  success: function(response) {
    console.log(response);
  },
  error: function(error) {
    console.error(error);
  }
});

With the native fetch() method, you pass the URL directly. Because fetch() returns a Promise, you handle the response sequentially:

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

Using async/await, the code becomes even more concise:

async function getData() {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Fetch error:', error);
  }
}

POST Request with Fetch

To send data via POST, pass a configuration object as the second argument to fetch(), specifying the HTTP method, headers, and the stringified body payload:

async function postData() {
  try {
    const response = await fetch('https://api.example.com/posts', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        title: 'New Post',
        body: 'Content goes here'
      })
    });

    const result = await response.json();
    console.log(result);
  } catch (error) {
    console.error('Request failed:', error);
  }
}

Key Differences to Keep in Mind

  1. HTTP Error Handling: jQuery's $.ajax() automatically triggers the error callback for HTTP error statuses such as 404 or 500. Native fetch() only rejects a Promise if there is a network failure or if the request is blocked. To detect HTTP errors, you must manually check response.ok (which returns true for status codes in the 200–299 range).
  2. Body Parsing: jQuery automatically parses JSON responses. Native fetch() requires an explicit step to parse the body using methods like response.json(), response.text(), or response.blob().
  3. Credentials/Cookies: By default, native fetch() includes credentials (such as cookies) for same-origin requests. For cross-origin requests requiring cookies, you must explicitly set credentials: 'include'.

The Legacy Alternative: XMLHttpRequest

Before the Fetch API existed, the native alternative to $.ajax() was the XMLHttpRequest (XHR) object. While XHR is still supported across all browsers, it requires verbose, event-based syntax and is generally discouraged in favor of fetch() for new projects.