Network Requests

Fetch API & AJAX

Toda aplicación web moderna se comunica con un servidor. La Fetch API es la forma estándar de enviar solicitudes, leer respuestas y actualizar la página sin necesidad de recargarla por completo.

intermediate15 min readUpdated 15 sept 2026
js
// api.js
async function getUsers() {
  const res = await fetch("/api/users", {
    headers: { Accept: "application/json" },
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

const users = await getUsers();
console.log(users);
Estándar
Parte del Fetch Living Standard
Retorna
Una Promise<Response>
Reemplaza a
XMLHttpRequest
Leer cuerpo como
json, text, blob, formData
Cancelar con
AbortController
Rechaza en
Solo errores de red

Practico

Prueba una solicitud fetch

Esta demo solicita una URL de datos JSON, por lo que funciona offline.

Prueba una solicitud fetch

Esta demo solicita una URL de datos JSON, por lo que funciona offline.

Por que importa

Por qué fetch es importante

Una sola API para todo

La misma llamada a fetch gestiona APIs JSON, texto, archivos, streams y datos de formulario en todos los navegadores y runtimes.

Basada en Promesas

Fetch retorna una promesa, por lo que se compone perfectamente con async/await y combinadores de Promise.

Segura por defecto

Valores predeterminados sensatos sobre CORS y credenciales te ayudan a evitar fugas de datos accidentales.

La imagen completa

Las tres partes de cada solicitud

Una solicitud de red consiste en una petición, una respuesta y la gestión de errores a su alrededor. Domina estos tres puntos y el resto serán detalles.

La solicitud

Enviar

La URL, el método, los headers y el cuerpo describen exactamente qué quieres que haga el servidor.

La respuesta

Leer

El estado, los headers y un cuerpo que puedes parsear como JSON, texto, un blob o un stream.

Los errores

Recuperar

Distingue los fallos de red de los estados de error HTTP, y luego muestra algo útil al usuario.

Fetch de un vistazo

Lo que te ofrece la Fetch API

Solicitudes GET

Obtén datos de una URL y lee el cuerpo de la respuesta.

POST & PUT

Envía datos al servidor definiendo un método, headers y un cuerpo.

Lectura de JSON

res.json() parsea el cuerpo de la respuesta en un valor de JavaScript.

Gestión de errores

Verifica res.ok para errores HTTP y usa catch para fallos de red.

Cancelación

AbortController detiene las solicitudes que ya no son necesarias.

CORS & credenciales

Comprende los orígenes, las solicitudes preflight y cuándo se envían las cookies.

Una breve historia

De XMLHttpRequest a fetch

  1. 1999

    XMLHttpRequest

    Microsoft introduce XHR y comienza la era de AJAX.

    99
  2. 2005

    AJAX se vuelve mainstream

    Gmail y Google Maps demuestran que las páginas pueden actualizarse sin recargar.

    05
  3. 2015

    La Fetch API

    Llega a los navegadores un reemplazo más limpio y basado en promesas para XHR.

    15
  4. 2017

    async / await

    Fetch se vuelve drásticamente más legible gracias a la nueva sintaxis.

    17
  5. Hoy

    El estándar

    Fetch es la API de solicitudes estándar en navegadores, Node.js, Deno y runtimes de edge.

    Hoy

La guia completa

Fetch API & AJAX: Todo lo que necesitas saber

¿Qué es AJAX?

AJAX significa Asynchronous JavaScript and XML, un término acuñado en 2005 cuando Gmail y Google Maps demostraron por primera vez que una página podía obtener nuevos datos sin necesidad de recargarse por completo. La parte de XML es básicamente historia —las APIs modernas devuelven JSON— pero la idea central sigue siendo la misma: comunicarse con un servidor en segundo plano y actualizar la página en el lugar.

La herramienta original era XMLHttpRequest. Funciona, pero su API es incómoda y se basa en callbacks. El reemplazo moderno es la Fetch API, una interfaz basada en promesas disponible en navegadores, Node.js, Deno y runtimes de edge.

Tu primera solicitud fetch

fetch recibe una URL y devuelve una promesa de un Response.

// basic.js
const response = await fetch("https://api.example.com/users");
const users = await response.json();

console.log(users);

Eso es todo el flujo ideal. El detalle es que fetch se resuelve incluso para códigos de estado de error, por lo que un 404 no lanza una excepción. Tienes que inspeccionar la respuesta tú mismo.

El objeto Response

El objeto Response te indica qué fue lo que el servidor envió de vuelta.

// response.js
const response = await fetch("/api/users");

response.ok;      // true for status 200–299
response.status;  // 200, 404, 500, ...
response.statusText;
response.headers.get("content-type");

const data = await response.json();   // parse JSON
const text = await response.text();   // raw text
const blob = await response.blob();   // binary data
const form = await response.formData();

El cuerpo (body) solo puede leerse una vez. Si necesitas tanto el texto plano como el JSON parseado, llama a response.clone() antes de leer, o parsea el texto tú mismo. Ten en cuenta también que response.json() falla si el cuerpo no es un JSON válido; otra razón más para envolver las llamadas en try/catch.

Cómo manejar los errores correctamente

Existen dos tipos de fallos y se comportan de manera diferente.

  1. Errores de red — sin conexión, fallo de DNS, CORS bloqueado. fetch se rechaza (rejects), por lo que catch se encarga de manejarlos.
  2. Errores de HTTP — 404, 401, 500. fetch se resuelve (resolves); debes verificar response.ok o response.status.
// errors.js
async function getUser(id) {
  try {
    const response = await fetch(`/api/users/${id}`);
    if (!response.ok) {
      throw new Error(`Request failed with status ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error("Could not load user:", error);
    throw error;
  }
}

Tratar ambos casos de forma explícita es lo que diferencia un código frágil de un código resiliente. La guía de Manejo de Errores cubre la estrategia general.

Envío de datos con POST, PUT y DELETE

Pasa un objeto de opciones para cambiar el método, añadir headers y adjuntar un cuerpo.

// create.js
async function createUser(user) {
  const response = await fetch("/api/users", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
    },
    body: JSON.stringify(user),
  });

  if (!response.ok) throw new Error("Could not create user");
  return response.json();
}

await createUser({ name: "Ada", role: "engineer" });

Usa PUT para reemplazar un recurso, PATCH para actualizar una parte del mismo y DELETE para eliminarlo. Para la subida de archivos, construye un objeto FormData y pásalo como el cuerpo; el navegador configurará automáticamente el content type multipart correcto por ti.

Cabeceras y autenticación

Las cabeceras transportan metadatos sobre la solicitud. Puedes definirlas por solicitud, y muchas API esperan un token de autorización.

// auth.js
const response = await fetch("/api/me", {
  headers: {
    Authorization: `Bearer ${token}`,
    Accept: "application/json",
  },
});

Nunca escribas secretos directamente en el código del lado del cliente; cualquier cosa que se envíe al navegador es pública. Los tokens deben provenir de un flujo de inicio de sesión y almacenarse con cuidado.

Cancelar solicitudes

Una solicitud que ya no es relevante —porque el usuario escribió otro carácter, navegó a otra página o el componente se desmontó— debe ser cancelada. AbortController hace exactamente eso.

// abort.js
const controller = new AbortController();

fetch("/api/search?q=javascript", { signal: controller.signal })
  .then((res) => res.json())
  .then(console.log)
  .catch((error) => {
    if (error.name === "AbortError") return;
    console.error(error);
  });

// Cancel when it is no longer needed
controller.abort();

Debido a que fetch no tiene una opción de timeout, AbortController es también la forma estándar de implementar uno.

// timeout.js
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 5000);

try {
  const res = await fetch("/api/slow", { signal: controller.signal });
  return await res.json();
} finally {
  clearTimeout(timer);
}

CORS y credenciales

Los navegadores aplican la política de mismo origen (same-origin policy): JavaScript no puede leer respuestas de un origen diferente a menos que el servidor lo permita. Ese permiso es CORS, y se configura enteramente en el servidor a través de encabezados de respuesta como Access-Control-Allow-Origin. Si ves un error de CORS, la solución debe aplicarse en la configuración del servidor, no en tu llamada a fetch.

Por defecto, fetch no envía cookies a URLs de orígenes cruzados. Para incluirlas, establece credentials: "include" y asegúrate de que el servidor permita solicitudes con credenciales. Esta es una fuente común de confusión del tipo “funciona en Postman pero no en el navegador”.

Patrones prácticos

Estados de carga, éxito y error. Refleja siempre el ciclo de vida de la solicitud en la UI.

// state.js
async function loadPosts() {
  showSpinner();
  try {
    const res = await fetch("/api/posts");
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    renderPosts(await res.json());
  } catch (error) {
    showError("Could not load posts. Try again.");
  } finally {
    hideSpinner();
  }
}

Búsqueda con debounce. Cancela la solicitud anterior cada vez que comience una nueva, para evitar que respuestas lentas sobrescriban resultados recientes. Combina AbortController con un pequeño retraso de debounce.

Solicitudes paralelas. Usa Promise.all cuando necesites varios endpoints a la vez, y Promise.allSettled cuando un resultado parcial siga siendo útil.

Mejores prácticas

  • Verifica siempre response.ok antes de analizar el cuerpo (body).
  • Establece un encabezado Accept explícito y un Content-Type al enviar un cuerpo.
  • Envuelve las solicitudes asíncronas en try/catch y muestra un mensaje útil.
  • Cancela las solicitudes obsoletas con AbortController.
  • Mantén las llamadas a la API en un módulo pequeño en lugar de dispersar las URLs por los componentes.
  • Nunca pongas secretos en el código del lado del cliente.
  • Muestra estados de carga y de error para que la UI nunca parezca rota.

Errores comunes

  • Asumir que fetch lanza una excepción en un 404 o 500.
  • Olvidar JSON.stringify en el cuerpo de una solicitud.
  • Omitir el encabezado Content-Type y recibir un error de parseo del servidor.
  • Leer el cuerpo de una respuesta dos veces.
  • Ignorar CORS hasta que el navegador bloquea la solicitud en producción.
  • Dejar solicitudes sin cancelar y permitir que datos obsoletos ganen una carrera (race condition).

Próximos pasos

Fetch es el puente entre tu front end y el mundo. Combínalo con el DOM para renderizar resultados, async/await para secuenciar el trabajo y el manejo de errores para gestionar los fallos de forma controlada. A partir de ahí, profundiza en tu comprensión de los métodos, códigos de estado y headers, ya que estos definen cada solicitud que envíes.

Verificación de la respuesta

fetch solo rechaza en caso de fallo de red. Un 404 o 500 sigue resolviéndose, así que verifica res.ok.

Preferido
const res = await fetch("/api/data");
if (!res.ok) {
  throw new Error(`HTTP ${res.status}`);
}
const data = await res.json();
Evitar
const res = await fetch("/api/data");
const data = await res.json();
// silently parses error pages

Envío de JSON

Indica al servidor qué estás enviando y convierte el cuerpo a cadena de texto.

Preferido
await fetch("/api/users", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ name: "Ada" }),
});
Evitar
await fetch("/api/users", {
  method: "POST",
  body: { name: "Ada" },
});

Preguntas frecuentes

Preguntas frecuentes

Keep learning

Related topics from the roadmap.

$ comienza a aprender

Listo para aprender Fetch API & AJAX?

Nuestro tutorial interactivo te guia a traves de Fetch API & AJAX paso a paso — con quizzes y codigo real que puedes ejecutar en el navegador.