Skip to content

XMLHttpRequest to fetch #520

Open
Open

Description

@avivkeller

This isn't really node specific, but XMLHttpRequest has long been all-but-replaced by fetch, and it would be nice to add a migration to allow these legacy codebases to run in modern environments, including Node.js

Before:

const req = new XMLHttpRequest();
req.addEventListener("load", () => console.log(this.responseText));
req.open("GET", "http://www.example.org/example.txt");
req.send();

After:

const req = await fetch("http://example.org");
console.log(req.text());

Activity

  1. AugustinMauroy commented on Aug 2, 2026

    @AugustinMauroy
    Member

    Definitely good idea ! What about the codemod name something like @nodejs/xhr-to-fetch ?

    Here the list of all before/after that i had found with migratable patterns

    1. Basic GET request

    Before:

    const req = new XMLHttpRequest();
    
    req.addEventListener("load", function () {
      console.log(this.responseText);
    });
    
    req.open("GET", "https://api.example.org/users");
    req.send();

    After:

    const res = await fetch("https://api.example.org/users");
    const data = await res.text();
    
    console.log(data);

    2. Parsing JSON

    Before:

    const req = new XMLHttpRequest();
    
    req.open("GET", "https://api.example.org/users");
    
    req.addEventListener("load", function () {
      const users = JSON.parse(this.responseText);
      console.log(users);
    });
    
    req.send();

    After:

    const res = await fetch("https://api.example.org/users");
    const users = await res.json();
    
    console.log(users);

    3. POST request with JSON

    Before:

    const req = new XMLHttpRequest();
    
    req.open("POST", "https://api.example.org/users");
    req.setRequestHeader("Content-Type", "application/json");
    
    req.addEventListener("load", function () {
      console.log(this.responseText);
    });
    
    req.send(JSON.stringify({
      name: "Alice",
      email: "alice@example.org"
    }));

    After:

    const res = await fetch("https://api.example.org/users", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        name: "Alice",
        email: "alice@example.org"
      })
    });
    
    console.log(await res.text());

    4. Custom headers

    Before:

    const req = new XMLHttpRequest();
    
    req.open("GET", "https://api.example.org/data");
    req.setRequestHeader("Authorization", "Bearer token");
    
    req.addEventListener("load", function () {
      console.log(this.responseText);
    });
    
    req.send();

    After:

    const res = await fetch("https://api.example.org/data", {
      headers: {
        Authorization: "Bearer token"
      }
    });
    
    console.log(await res.text());

    5. Handling errors

    Before:

    const req = new XMLHttpRequest();
    
    req.open("GET", "https://api.example.org/data");
    
    req.addEventListener("load", function () {
      if (this.status >= 200 && this.status < 300) {
        console.log(this.responseText);
      } else {
        console.error(`Request failed: ${this.status}`);
      }
    });
    
    req.addEventListener("error", () => {
      console.error("Network error");
    });
    
    req.send();

    After:

    try {
      const res = await fetch("https://api.example.org/data");
    
      if (!res.ok) {
        throw new Error(`Request failed: ${res.status}`);
      }
    
      console.log(await res.text());
    } catch (err) {
      console.error("Network error", err);
    }

    6. Query parameters

    Before:

    const req = new XMLHttpRequest();
    
    req.open(
      "GET",
      `https://api.example.org/users?name=${encodeURIComponent("Alice")}`
    );
    
    req.addEventListener("load", function () {
      console.log(this.responseText);
    });
    
    req.send();

    After:

    const url = new URL("https://api.example.org/users");
    url.searchParams.set("name", "Alice");
    
    const res = await fetch(url);
    console.log(await res.text());

    7. Abort / timeout

    Before:

    const req = new XMLHttpRequest();
    
    req.open("GET", "https://api.example.org/data");
    req.timeout = 5000;
    
    req.addEventListener("load", function () {
      console.log(this.responseText);
    });
    
    req.addEventListener("timeout", () => {
      console.error("Request timed out");
    });
    
    req.send();

    After:

    const signal = AbortSignal.timeout(5000);
    
    try {
      const res = await fetch("https://api.example.org/data", { signal });
      console.log(await res.text());
    } catch (err) {
      console.error("Request timed out", err);
    }

    8. Response status and headers

    Before:

    const req = new XMLHttpRequest();
    
    req.open("GET", "https://api.example.org/data");
    
    req.addEventListener("load", function () {
      console.log(this.status);
      console.log(this.getResponseHeader("content-type"));
      console.log(this.responseText);
    });
    
    req.send();

    After:

    const res = await fetch("https://api.example.org/data");
    
    console.log(res.status);
    console.log(res.headers.get("content-type"));
    console.log(await res.text());

    9. Downloading binary data

    Before:

    const req = new XMLHttpRequest();
    
    req.open("GET", "https://api.example.org/file");
    req.responseType = "arraybuffer";
    
    req.addEventListener("load", function () {
      const buffer = this.response;
      console.log(buffer);
    });
    
    req.send();

    After:

    const res = await fetch("https://api.example.org/file");
    const buffer = await res.arrayBuffer();
    
    console.log(buffer);
  2. moved this from 📋 Backlog to 🔖 Todo in Userland Migrations Boardon Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions