AJAX in JavaScript

AJAX stands for Asynchronous JavaScript and XML. Today, AJAX is a web development technique for communicating with a server asynchronously and updating part of a web page without reloading the entire page. It does not require XML; JSON is commonly used instead.

JavaScript running in the browser can implement AJAX-style communication with APIs using tools such as fetch() and XMLHttpRequest. The page can then update the relevant content when the response arrives.

Before AJAX, every time you needed new data, you had to reload the entire page. That was slow and annoying. AJAX changed that by letting JavaScript talk to the server in the background while you keep browsing.

  • Load new content without a page refresh
  • Send data to a server (like a form submission) without navigating away
  • Check for updates in the background (like live notifications)
Quick history: AJAX was popularized around 2005 by Google Maps, which was blowing people's minds by loading map tiles without a page reload. The "XML" part in the name is old - today most apps use JSON instead.

Every time you open a website, two things are involved - a client and a server.

The client is your browser (Chrome, Firefox, Safari). It's the thing asking for stuff. The server is a computer somewhere else that has the data and sends it back to you.

The browser sends an HTTP request to a server or API over the network. The server receives and processes the request, sends an HTTP response, and JavaScript can use that response to update the page without a full reload.

  • Client - your browser, makes requests, shows the result
  • Server - a computer that receives requests and sends back data
  • Network - the internet connection between them

When you use AJAX, JavaScript on the client side sends a request to the server, the server processes it and sends back a response, and then JavaScript updates the page with the new data - all without you having to click a refresh button.

Good to know: The server doesn't know or care what browser you're using. It just receives a request and sends back data. How that data is shown is entirely up to your JavaScript code on the client side.

Every conversation between a client and server happens in two parts: a request and a response.

Beginner mental model: request means "please do this," and response means "here is what happened." A slow response should show loading feedback; a failed response should show a useful message.

The request is what the client sends. It includes:

  • Method - what you want to do (GET, POST, PUT, DELETE)
  • URL - where to send the request (e.g. https://api.example.com/users)
  • Headers - extra info like content type or authentication tokens
  • Body - the actual data you're sending (when the method supports a request body, such as POST, PUT, or PATCH)

The response is what the server sends back. It includes:

  • Status code - a number that tells you if the request worked (e.g. 200 = OK, 404 = Not Found, 500 = Server Error)
  • Headers - info about the response (like content type)
  • Body - the actual data (usually JSON)

The most common HTTP methods you'll use are:

  • GET - fetch data (read-only, no body needed)
  • POST - send new data to the server (like creating a user)
  • PUT - update existing data
  • DELETE - remove data
Status codes to remember: 200 (OK), 201 (Created), 400 (Bad Request), 401 (Unauthorized), 403 (Forbidden), 404 (Not Found), 500 (Internal Server Error).

API stands for Application Programming Interface. That sounds complicated, but the idea is simple - an API is just a way for two programs to talk to each other.

When you're building a web app, you often need data from somewhere else - weather info, user data, product listings, etc. Instead of building all that yourself, you can use an API that someone else built. You ask for the data, and they give it to you in a format your code can understand.

An API provides a defined interface through which one program can communicate with another program or service. For web APIs, clients commonly communicate with servers over HTTP using documented URLs, methods, and data formats.

javascript

// A public API that returns fake user data for testing
// URL: https://jsonplaceholder.typicode.com/users/1
// Response (JSON):
// {
//   "id": 1,
//   "name": "Leanne Graham",
//   "email": "Sincere@april.biz",
//   "phone": "1-770-736-0988"
// }

APIs often return data in JSON format (JavaScript Object Notation). JSON resembles JavaScript object and array syntax, but it is text with stricter rules, so the browser or your code must parse it before you can use it as a JavaScript value.

Examples of popular public APIs you can try: JSONPlaceholder (fake data for testing), OpenWeatherMap (weather data), GitHub API (repository and user info).

Web API vs JavaScript API: "API" can mean two things. A Web API is an interface exposed by a service and commonly accessed over HTTP. A JavaScript API is a built-in browser feature like the DOM or Fetch. In this tutorial, we're talking about web APIs accessed with AJAX.

REST stands for Representational State Transfer. It is an architectural style for designing networked APIs so that resources and their operations can be represented consistently.

The core idea is simple: each URL represents a resource (a thing), and you use different HTTP methods to do different things to that resource.

text

# REST-style API examples for a "users" resource:

GET    /users          # get all users
GET    /users/1        # get user with id 1
POST   /users          # create a new user
PUT    /users/1        # update user with id 1
DELETE /users/1        # delete user with id 1

The URL identifies the resource (users), and the HTTP method describes the operation (reading, creating, updating, or deleting). These are common REST principles, although using HTTP methods and resource-based URLs alone does not automatically make an API fully RESTful.

Common REST principles include:

  • Stateless - each request must contain all the information needed. The server doesn't remember your previous requests.
  • Uniform interface - use standard HTTP methods (GET, POST, PUT, DELETE) consistently
  • Resource-based URLs - URLs should be nouns, not verbs. Use /users not /getUsers
  • Use HTTP status codes - 200 for success, 404 for not found, etc.
Practical context: REST is one common style for designing HTTP APIs. Other API styles also exist, so check the documentation for the service you are using.

CORS stands for Cross-Origin Resource Sharing. If you've ever seen an error in the console that says something like "has been blocked by CORS policy", this is what it's about.

Browsers enforce the Same-Origin Policy, a security mechanism that restricts how browser JavaScript reads resources from another origin. For cross-origin requests, CORS allows a server to specify which origins are permitted to read the response.

For example, if your website is on myapp.com and you try to read data from api.someothersite.com, the browser checks the server's CORS response headers. The browser enforces the policy, while the server grants controlled access by sending those headers.

A CORS failure does not simply mean that the server rejected the HTTP request. The request may reach the server, but browser JavaScript may be prevented from accessing the response when the CORS requirements are not satisfied.

text

# A server response header that allows any origin to access the API:
Access-Control-Allow-Origin: *

// Or allow only a specific site:
Access-Control-Allow-Origin: https://myapp.com

If the server does not include an appropriate header, the browser can block JavaScript from reading the response even if the request went through. The browser reports the CORS failure; the server provides the CORS response headers.

As a frontend developer, you usually can't fix CORS errors yourself - the server has to allow your origin. But during development, there are a few common workarounds:

  • Use a proxy (your dev server forwards the request to avoid cross-origin issues)
  • Ask the backend developer to add the right CORS headers
  • Use a public API that already allows cross-origin requests (like JSONPlaceholder)
CORS is a browser thing. Tools like Postman or curl don't have the same-origin policy, so they will work fine even when your browser throws a CORS error. The restriction only applies when JavaScript in a browser tries to make the request.
Note: The Fetch API is the modern, recommended way to make HTTP requests in JavaScript. It has a cleaner syntax, uses Promises, and is supported in all current browsers. XMLHttpRequest is covered here so you can understand and work with legacy codebases - for any new code you write, prefer the Fetch API.

XMLHttpRequest (often shortened to XHR) is the original way JavaScript talks to servers. It was introduced in the early 2000s and was the foundation of AJAX for many years. Even though modern code uses the Fetch API now, it's worth knowing how XHR works because you'll see it in older codebases.

Here's the basic pattern - it's a bit verbose compared to modern approaches:

javascript

// Step 1: Create a new XMLHttpRequest object
const xhr = new XMLHttpRequest();

// Step 2: Configure the request (method + URL)
xhr.open("GET", "https://jsonplaceholder.typicode.com/posts/1");

// Step 3: Define what happens when the response arrives
xhr.onload = function () {
  if (xhr.status === 200) {
    const data = JSON.parse(xhr.responseText);
    console.log(data.title); // sunt aut facere repellat...
  } else {
    console.error("Request failed: " + xhr.status);
  }
};

// Step 4: Send the request
xhr.send();

The key property is readyState - it tells you what stage the request is at:

  • 0 - Not sent yet (UNSENT)
  • 1 - Connection opened (OPENED)
  • 2 - Request sent, headers received (HEADERS_RECEIVED)
  • 3 - Downloading response (LOADING)
  • 4 - Done (DONE)

javascript

const xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/posts/1");

// onreadystatechange fires every time readyState changes
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    const post = JSON.parse(xhr.responseText);
    console.log(post.title);
  }
};

xhr.send();
XHR vs Fetch: XHR is event-based and a bit clunky to read. The Fetch API (covered in the next tutorial) does the same thing with a much cleaner, Promise-based syntax. In new code, prefer Fetch. But knowing XHR means you can understand and maintain older projects.

Let's put it all together and make a real API call with XMLHttpRequest. We'll use JSONPlaceholder - a free, public API that returns fake data, perfect for learning and testing.

javascript

// GET request - fetch a list of posts
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/posts");

xhr.onload = function () {
  if (xhr.status === 200) {
    const posts = JSON.parse(xhr.responseText);
    console.log("Total posts:", posts.length); // 100
    console.log("First post title:", posts[0].title);
  }
};

xhr.send();

You can also send a POST request to create data. You need to set the Content-Type header and pass the data in the body:

javascript

// POST request - send new data
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://jsonplaceholder.typicode.com/posts");

// Tell the server we're sending JSON
xhr.setRequestHeader("Content-Type", "application/json");

xhr.onload = function () {
  if (xhr.status === 201) {
    const newPost = JSON.parse(xhr.responseText);
    console.log("Created post with id:", newPost.id); // 101
  }
};

const postData = {
  title: "My new post",
  body: "This is the content",
  userId: 1
};

xhr.send(JSON.stringify(postData));
Tip: Always call JSON.stringify() before sending an object in the request body, and JSON.parse() after receiving the response text. That's how you convert between JavaScript objects and JSON strings.

Once your AJAX request completes, you need to handle what comes back. There are three things that can happen: success, a server error, or a network failure. Good code handles all three.

javascript

const xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/users/1");

// Called when the request completes (success or server error)
xhr.onload = function () {
  if (xhr.status >= 200 && xhr.status < 300) {
    // Success
    const user = JSON.parse(xhr.responseText);
    console.log("Name:", user.name);
    console.log("Email:", user.email);
  } else {
    // Server returned an error (404, 500, etc.)
    console.error("Server error:", xhr.status, xhr.statusText);
  }
};

// Called only if the network itself failed (no internet, bad URL, etc.)
xhr.onerror = function () {
  console.error("Network error - request could not be sent");
};

xhr.send();

Handle timeouts and invalid JSON

A 404 or 500 response is different from a network failure: the server replied, but it reported a problem. Parsing can fail too, so protect JSON.parse() with try...catch.

javascript

const xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/users/1");
xhr.timeout = 5000;

xhr.onload = function () {
  if (xhr.status < 200 || xhr.status >= 300) {
    console.error("Server error:", xhr.status);
    return;
  }

  try {
    const user = JSON.parse(xhr.responseText);
    console.log(user.name);
  } catch (error) {
    console.error("The server returned invalid JSON.");
  }
};

xhr.onerror = function () {
  console.error("Network or CORS error.");
};

xhr.ontimeout = function () {
  console.error("The request timed out. Try again.");
};

xhr.send();

Use the same decision model in your interface: show the data for a successful 2xx response, explain what went wrong for an HTTP error, and offer a retry or connection check for network and timeout failures.

You can show a basic loading state immediately and use the onprogress event to display download progress when the response provides a measurable size:

javascript

const xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/photos"); // ~5000 items

// Show loading feedback
document.getElementById("status").textContent = "Loading...";

xhr.onprogress = function (event) {
  if (event.lengthComputable) {
    const percent = Math.round((event.loaded / event.total) * 100);
    document.getElementById("status").textContent = `Loading... ${percent}%`;
  }
};

xhr.onload = function () {
  if (xhr.status < 200 || xhr.status >= 300) {
    document.getElementById("status").textContent = "Request failed.";
    return;
  }
  document.getElementById("status").textContent = "Done!";
  const photos = JSON.parse(xhr.responseText);
  console.log("Loaded photos:", photos.length);
};

xhr.onerror = function () {
  document.getElementById("status").textContent = "Failed to load.";
};

xhr.send();

A few important properties to know when reading a response:

  • xhr.status - the HTTP status code (200, 404, 500, etc.)
  • xhr.statusText - the status message ("OK", "Not Found", etc.)
  • xhr.responseText - the raw response body as a string
  • xhr.responseType - set this to "json" and the browser parses it for you automatically

javascript

// Using responseType "json" - no need to JSON.parse manually
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/users/1");
xhr.responseType = "json"; // browser parses JSON automatically

xhr.onload = function () {
  if (xhr.status === 200) {
    // xhr.response is already a JavaScript object
    console.log(xhr.response.name);  // Leanne Graham
    console.log(xhr.response.email); // Sincere@april.biz
  }
};

xhr.send();
Always handle errors. A lot of beginners only write the happy path. In real projects, things go wrong - the server goes down, the user has no internet, the URL changes. Make sure your code has an onerror handler and checks the status code.
  • AJAX lets you load or send data in the background without refreshing the page
  • Every web interaction involves a client (your browser) and a server (a computer somewhere else)
  • A request includes a method, URL, headers, and optionally a body. A response includes a status code, headers, and a body
  • Common HTTP methods: GET (read), POST (create), PUT (update), DELETE (remove)
  • An API is a defined interface that lets programs communicate; a web API often exposes server data over HTTP
  • REST is an architectural style for designing networked APIs with resource-based URLs and HTTP methods
  • CORS is a mechanism for servers to grant controlled cross-origin access, enforced by browsers
  • XMLHttpRequest (XHR) is the original AJAX tool - event-based and verbose, but still found in older codebases
  • Always handle both success and error cases in your response handlers

What's next? Now that you understand how AJAX and the request/response cycle work, learn the modern way to do it in the Fetch API tutorial.

Practice the request lifecycle

There is not a dedicated AJAX exercise yet. Reinforce the same skills with error-handling practice, browse the exercise and quiz collections, then continue with Fetch.

Videos for this topic will be added soon.

Reviewed by

SimplyJavaScript Editorial Team

Technical editors and JavaScript educators with hands-on experience building frontend projects, writing learning material, and reviewing tutorials for clarity, accuracy, and beginner-friendly guidance.