Enviar datos con Fetch API en JavaScript

Aprende a enviar datos con Fetch API de forma eficiente. En esta guía técnica exploramos cómo configurar peticiones HTTP POST, gestionar el objeto JSON y manipular las headers para una comunicación segura entre cliente y servidor. Domina el uso de Async/Await para optimizar el envío de formularios y el procesamiento de respuestas en aplicaciones web modernas.

enviar datos fetch api javascript

1. ¿Qué significa enviar datos con Fetch?

Enviar datos con Fetch API en JavaScript significa realizar una petición HTTP desde el navegador hacia un servidor para transferir información, generalmente utilizando métodos como POST, PUT o PATCH. Este proceso es fundamental en el desarrollo web moderno, ya que permite que las aplicaciones interactúen con APIs y bases de datos sin recargar la página.

A diferencia de una petición GET con Fetch API (que solo obtiene información), cuando enviamos datos con estamos transmitiendo información desde el cliente al backend, como formularios, credenciales de usuario o contenido dinámico. Estos datos suelen enviarse en el body de la petición, comúnmente en formato JSON o FormData.

En JavaScript moderno, este proceso se realiza utilizando Fetch API junto con async/await, lo que permite escribir código más limpio, legible y fácil de mantener. Además, se pueden configurar aspectos clave como los headers, el tipo de contenido (Content-Type) y la estructura de los datos enviados.

En términos prácticos, enviar datos con fetch implica:

  • Definir el método HTTP adecuado (POST, PUT, PATCH)
  • Preparar los datos a enviar (JSON.stringify o FormData)
  • Configurar la petición con headers y opciones
  • Procesar la respuesta del servidor

Este flujo es esencial para crear aplicaciones web interactivas, dinámicas y conectadas a servicios externos.

1.1. Casos comunes y reales donde se envían datos con Fetch

En el desarrollo web moderno, enviar datos es una acción constante para mantener la interactividad. Aquí te presento los escenarios más frecuentes donde aplicarás esta tecnología:

Formularios de Registro e Inicio de Sesión
Es el uso más crítico. Capturamos los datos que el usuario escribe (email, contraseña) y los enviamos al servidor para su validación o almacenamiento en una base de datos. Es esencial para la autenticación de usuarios.
Envío de Formularios de Contacto
En lugar de recargar la página tras hacer clic en «Enviar», usamos Fetch para mandar el mensaje y mostrar un aviso de «¡Éxito!» al instante mediante el DOM. Permite una experiencia más fluida sin recargas.
Pasarelas de Pago y Carritos de Compra
Cuando un usuario añade un producto o confirma una compra, Fetch comunica al servidor los IDs de los artículos y los detalles de la transacción de forma segura. Es clave en procesos de compra online.
Actualización de Perfil de Usuario
Al cambiar tu foto o biografía en una red social, se realiza una petición (usualmente PUT o PATCH) que actualiza solo esa porción de información en el servidor. Permite modificar datos sin recargar toda la aplicación.
Sistemas de Comentarios y «Likes»
Cada vez que das «me gusta» a una publicación o escribes una respuesta, estás enviando una pequeña cantidad de datos mediante Fetch para que ese cambio se guarde permanentemente. Hace posible la interacción en tiempo real.
Carga de Archivos e Imágenes
Fetch permite enviar objetos de tipo Blob o File, facilitando que los usuarios suban documentos a la nube sin interrumpir su navegación. Es fundamental para la subida de archivos moderna.

2. ¿Cómo enviar datos con Fetch()?

Antes de sumergirnos en cómo enviar información a una base de datos, es vital recordar que la Fetch API es, por naturaleza, una herramienta de consulta.

Su función principal es conectar tu aplicación con una URL para intercambiar recursos.

Entonces, primero debemos conocer como es la sintaxis general de una petición con Fetch, con la cuál podemos recibir datos, para luego profundizar en cómo hacer para enviarlos:

fetch(
  URL,                // 1. Destino (Obligatorio): ¿A dónde vamos?
  { CONFIGURACIÓN }   // 2. Opciones (Opcional): ¿Cómo vamos y qué llevamos?
)
.then(respuesta => {
  // 3. Procesamiento: ¿Qué hacemos con lo que recibimos?
})
.catch(error => {
  // 4. Gestión de errores: ¿Qué hacemos si algo sale mal en la red?
});Lenguaje del código: JavaScript (javascript)

¿Por qué a veces no configuramos nada?

Una de las mayores ventajas de JavaScript es que simplifica las tareas más comunes. Si tú ejecutas un fetch(URL) y omites el segundo parámetro (el objeto de configuración), el navegador asume dos cosas de forma automática:

  1. El método es GET:
    • Asume que tu intención es únicamente recibir o leer datos desde el servidor.
  2. Sin cuerpo de datos:
    • Entiende que no estás enviando información adicional (como un usuario o una contraseña), sino que solo estás haciendo una petición de «solo lectura».

Es por esto que, para listar productos en un e-commerce o traer el clima actual, el código se ve tan limpio. Sin embargo, en el mundo real, las aplicaciones no solo leen información; también crean, actualizan y borran.

EL SIGUIENTE PASO: De recibir a ENVIAR

Ahora que tenemos claro que por defecto Fetch es un sistema de recepción de datos (GET), estamos listos para romper ese comportamiento estándar.

Para transformar una simple petición de lectura en una potente herramienta de envío de datos, necesitamos aprender a «llenar» ese objeto de CONFIGURACIÓN que mencionamos arriba.

En las siguientes secciones, veremos cómo decirle a Fetch que queremos usar otros métodos y cómo preparar el «paquete» de datos que queremos entregar al servidor.

2.1. El objeto de opciones (configuración):

Cuando queremos ir más allá del comportamiento por defecto, el segundo parámetro de fetch() —el objeto de opciones— (configuración en la sintaxis primera) se vuelve el protagonista. Aquí es donde definimos las reglas del juego para la comunicación con el servidor.

Estructura completa de configuración

A continuación, verás las propiedades más importantes que podés incluir. Aunque en el 90% de los casos usarás solo las tres primeras, conocer las demás te permitirá resolver problemas complejos de seguridad y rendimiento.

fetch(URL, {
  method: 'POST',           // 1. Método HTTP
  headers: {                // 2. Cabeceras de la petición
    'Content-Type': 'application/json',
    'Authorization': 'Bearer tu_token_aqui'
  },
  body: JSON.stringify({}), // 3. El cuerpo de los datos
  mode: 'cors',             // 4. Modo de la petición (CORS, no-cors, same-origin)
  credentials: 'include',   // 5. Manejo de cookies y autenticación
  cache: 'default',         // 6. Estrategia de caché del navegador
  signal: controller.signal // 7. Permite cancelar la petición (AbortController)
});Lenguaje del código: JavaScript (javascript)

Explicación técnica de cada propiedad:

  1. method:
    • Indica la acción a realizar. Por defecto es GET, pero para enviar datos usamos POST (crear), PUT (reemplazar) o PATCH (modificar parcialmente).
  2. headers:
    • Es un objeto con metadatos. El más común es Content-Type, que le avisa al servidor en qué formato viajan los datos (generalmente JSON).
  3. body:
    • Es la información real que enviamos. Puede ser un string JSON, un objeto FormData o incluso un archivo binario. Nota: No se puede usar en peticiones GET o HEAD.
  4. mode:
    • Define si la petición puede cruzar dominios (CORS). Es vital para consumir APIs que están en servidores diferentes al de tu web.
  5. credentials:
    • Indica si el navegador debe enviar automáticamente cookies o cabeceras de autenticación TLS con la petición.
  6. cache:
    • Controla cómo interactúa la petición con la caché del navegador (si debe buscar una copia guardada o pedir datos frescos siempre).
  7. signal:
    • Es una herramienta avanzada que permite «abortar» o cancelar la petición si el usuario navega a otra página o si tarda demasiado.

3. El atributo method

El atributo method en el objeto de configuración de Fetch determina qué tipo de operación queremos realizar sobre el recurso en la URL de destino.

En otras palabras;
Es la propiedad que define qué tipo de acción HTTP querés realizar sobre el servidor, o sea, qué queremos hacer con los datos:

  • Obtenerlos – GET (Por defecto)
  • Enviarlos – POST
  • Actualizarlos – PUT
  • Eliminarlos -DELETE

Por defecto, si no definís esta propiedad, Fetch ejecutará un método GET, y no profundizamos en este método ya que lo vimos en este artículo sobre Fetch API en JS.

3.1. Guía de uso: ¿Cuándo usar cada método?

Para que tu código sea profesional y semánticamente correcto, debés elegir el método según la acción que deseas ejecutar:

POST (Crear)

  • Es el método estándar para enviar datos que resultarán en la creación de un nuevo recurso en el servidor.
  • ¿Para qué sirve?: Se usa cuando el cliente (tu web) envía información que aún no existe en la base de datos.
  • Ejemplo real: Registrar un nuevo usuario, publicar un comentario o realizar una compra.

PUT (Reemplazar/Actualizar)

  • Se utiliza para actualizar un recurso existente, enviando la versión completa y actualizada del mismo.
  • ¿Para qué sirve?: Sirve para realizar una sustitución total. Si el recurso no existe, en algunos sistemas puede llegar a crearlo.
  • Ejemplo real: Editar un perfil de usuario donde se envían nuevamente todos los campos (nombre, apellido, bio) para sobreescribir los anteriores.

PATCH (Modificar parcialmente)

  • A diferencia de PUT, PATCH se utiliza para aplicar modificaciones parciales a un recurso.
  • ¿Para qué sirve?: Es más eficiente cuando solo queremos cambiar un dato específico sin necesidad de enviar todo el objeto de nuevo.
  • Ejemplo real: Cambiar únicamente la contraseña de un usuario o marcar una tarea como «completada» en una lista de pendientes.

3.2. Ejemplo práctico

fetch('https://api.ejemplo.com/usuarios', {
  method: 'POST'
});Lenguaje del código: JavaScript (javascript)

Acá le estás diciendo al servidor: “quiero enviar datos nuevos”

4. El atributo headers.

Es un objeto que permite definir metadatos de la petición HTTP, es decir, información adicional que describe cómo se envían los datos y cómo deben ser interpretados por el servidor.

En el envío de datos con Fetch, las cabeceras son obligatorias para que el servidor sepa cómo interpretar el flujo de bytes que le estás enviando.

4.1. Cómo configurar las Headers

Existen dos formas de definir las cabeceras en Fetch. La más común es mediante un objeto literal, pero la forma más profesional y escalable es usando el constructor Headers().

Opción 1: Objeto literal (La más usada)

Es ideal para peticiones rápidas y sencillas.

const opciones = {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token_de_seguridad'
  },
  body: JSON.stringify({ usuario: 'DevMaster' })
};Lenguaje del código: JavaScript (javascript)

Opción 2: Constructor new Headers() (Recomendado para apps grandes)

Permite manipular las cabeceras de forma dinámica (agregar, borrar o consultar).

const misCabeceras = new Headers();
misCabeceras.append('Content-Type', 'application/json');
misCabeceras.append('Accept', 'application/json');

fetch(URL, {
  method: 'POST',
  headers: misCabeceras,
  body: JSON.stringify({ id: 101 })
});Lenguaje del código: JavaScript (javascript)

4.2. Los encabezados más utilizados y su función

A continuación, los encabezados que todo desarrollador de JavaScript debe conocer para trabajar con APIs profesionales:

4.2.1. Content-Type (El más importante)

  • Indica al servidor el formato original de los datos que estás enviando en el body.
  • Uso común: application/json (para objetos JSON), text/plain (texto plano) o multipart/form-data (para subir archivos).

4.2.2. Authorization (Seguridad)

  • Se utiliza para enviar credenciales que demuestren que el usuario tiene permiso para realizar la acción.
  • Uso común: Bearer <token> (usado con JSON Web Tokens o JWT). Es esencial para proteger rutas privadas de una API.

4.2.3. Accept (Negociación de contenido)

  • Le dice al servidor qué tipo de formato de respuesta espera recibir el cliente.
  • Uso común: application/json. Si el servidor puede responder en varios formatos (XML o JSON), este encabezado le indica cuál prefieres.

4.2.4. Cache-Control (Rendimiento)

  • Define las reglas de almacenamiento en caché para la petición.
  • Uso común: no-cache (obliga a buscar datos frescos), max-age=3600 (permite usar datos guardados por una hora).

4.2.5. X-API-KEY (Personalizados)

  • Muchos servicios de terceros (como Google Maps o APIs de clima) requieren una clave de acceso única que no es un token de usuario.
  • Uso común: Identificar la aplicación que está haciendo la consulta para límites de cuota y facturación.

4.3. Buenas prácticas y Errores comunes

  1. Sensibilidad a mayúsculas:
    • Aunque las cabeceras HTTP suelen ser case-insensitive, por convención y legibilidad siempre se escriben como 'Content-Type'.
  2. No olvides el formato:
    • Si declarás application/json, asegurate de que tu body sea realmente un string generado con JSON.stringify(). Si envías un objeto de JS puro, la petición fallará.
  3. Seguridad:
    • Nunca pongas contraseñas directamente en las headers. Usá cabeceras de autorización (Authorization) con tokens seguros (JWT).

5. El atributo body

Si comparamos una petición Fetch con un servicio de mensajería, el body (cuerpo) es el contenido real dentro de la caja. Es la propiedad donde colocamos la información que queremos que el servidor reciba, procese y guarde en su base de datos.

5.1. ¿Para qué sirve el body?

Su función es transportar la «carga útil» (payload) de la petición. Sin el body, el servidor sabría qué quieres hacer (gracias al method) y quién eres (gracias a los headers), pero no tendría los datos para ejecutar la acción (por ejemplo, no sabría qué nombre de usuario quieres registrar).

NOTA!! El body solo se utiliza en métodos que permiten enviar datos, como POST, PUT y PATCH. Intentar usarlo en una petición GET arrojará un error, ya que GET solo debe solicitar información a través de la URL.

5.2. ¿Cómo usarlo correctamente? (El proceso de serialización)

Un error común es intentar pasar un objeto de JavaScript directamente al body. Los servidores no entienden objetos de JS «vivos»; solo entienden texto (strings).

Por eso, debemos serializar los datos. El proceso estándar es:

  1. Tener un objeto JS con los datos.
  2. Convertirlo a una cadena de texto JSON usando JSON.stringify().
  3. Pasarlo a la propiedad body.

5.3. Ejemplo práctico

Imagina que quieres enviar los datos de un nuevo artículo para tu blog:

// 1. Definimos nuestros datos como un objeto JS normal
const nuevoPost = {
  titulo: "Dominando Fetch API",
  autor: "Tu Nombre",
  contenido: "Hoy aprenderemos a usar el body..."
};

// 2. Configuramos la petición
fetch('https://api.tu-sitio.com/posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  // 3. Convertimos el objeto a texto JSON
  body: JSON.stringify(nuevoPost) 
})
.then(res => res.json())
.then(datos => console.log("Post creado:", datos));Lenguaje del código: JavaScript (javascript)

Explicación del ejemplo:

  1. JSON.stringify(nuevoPost):
    • Esta función transforma {titulo: "..."} en '{"titulo": "..."}'. Ahora los datos son una cadena de texto plana que puede viajar por los cables de internet sin corromperse.
  2. Sincronía con headers:
    • Al usar este método, es obligatorio que en tus headers hayas declarado 'Content-Type': 'application/json', para que el servidor sepa que debe hacer el proceso inverso (deserializar) al recibir el paquete.

5.4. Tipos de datos que acepta el body

Aunque JSON es el rey, el body es flexible y puede transportar otros formatos según el proyecto:

  • JSON: El más usado para APIs (como vimos arriba).
  • FormData: Ideal para enviar formularios con archivos o imágenes directamente desde el DOM.
  • Blob / Buffer: Para datos binarios puros (archivos, audio).
  • URLSearchParams: Para enviar datos como si fueran una cadena de consulta de formulario antiguo.

6. El atributo mode

Sirve para establecer las reglas de seguridad y los límites de alcance de tu petición.

Le indica al navegador si la comunicación se mantendrá dentro del mismo servidor o si tiene permiso para realizar una petición «transfronteriza» (a otro dominio).

6.1. ¿Para qué sirve el mode?

Su función principal es la seguridad. El navegador utiliza esta configuración para decidir si debe aplicar el protocolo CORS (Cross-Origin Resource Sharing). Esto evita que scripts maliciosos de un sitio web roben datos de otro sitio sin permiso.

6.2. Los 3 valores principales de mode

  1. cors (Valor por defecto):
    • Permite peticiones a otros dominios siempre que el servidor de destino lo autorice mediante cabeceras especiales. Es lo que usás para conectarte a APIs externas.
  2. no-cors:
    • Es un modo muy limitado. Solo permite peticiones básicas y no permite leer la respuesta del servidor desde JavaScript. Se usa a veces para «notificaciones» donde no te importa el resultado.
  3. same-origin:
    • Garantiza que la petición solo se realice al mismo dominio donde está alojada tu web. Si intentás llamar a una API externa, la petición fallará automáticamente.

6.3. Ejemplo práctico

Imagina que tu sitio web está en misitio.com y querés enviar datos a una API en api-externa.com.

// Ejemplo de configuración con modo CORS explícito
fetch('https://api-externa.com/usuarios', {
  method: 'POST',
  mode: 'cors', // Indicamos que vamos a otro dominio
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ nombre: "Dev" })
})
.then(response => {
  console.log("¿Petición exitosa?", response.ok);
})
.catch(error => {
  console.error("Error de CORS o Red:", error);
});Lenguaje del código: JavaScript (javascript)

Explicación del ejemplo:

  • Al poner mode: 'cors', el navegador envía primero una «pre-petición» (llamada OPTIONS) al servidor externo para preguntar: «¿Tengo permiso para enviarte estos datos?».
  • Si el servidor responde que sí, la petición real se ejecuta. Si el servidor no está configurado para aceptar peticiones de tu dominio, el navegador bloqueará la respuesta por seguridad.

7. El atributo credentials.

El atributo credentials le indica al navegador si debe incluir información de autenticación automática (como cookies, certificados TLS o cabeceras de autenticación HTTP) en la petición fetch.

7.1. ¿Para qué sirve credentials?

Por motivos de seguridad, los navegadores suelen ser restrictivos con el envío de cookies a otros dominios para evitar ataques de tipo CSRF (Cross-Site Request Forgery).

Esta propiedad permite al desarrollador decidir explícitamente si quiere compartir la sesión del usuario con el servidor al que está enviando los datos.

7.2.Los 3 valores de credentials

  1. same-origin (Valor por defecto en muchos navegadores):
    • Solo envía cookies y datos de autenticación si la petición se realiza al mismo dominio donde está alojada tu web.
  2. include:
    • Envía siempre las credenciales, incluso si la petición es a un dominio diferente (CORS). Es fundamental si tu API está en un subdominio o servidor externo y necesitas mantener la sesión iniciada.
  3. omit:
    • Nunca envía credenciales ni permite que el servidor guarde cookies que vengan en la respuesta. Es la opción más privada.

7.3. Ejemplo práctico

Imagina que tienes una aplicación donde el usuario ya inició sesión y quieres enviar un comentario. El servidor necesita la cookie de sesión para saber quién escribe.

fetch('https://api.tu-sitio.com/comentarios', {
  method: 'POST',
  // Configuramos credentials para incluir la sesión del usuario
  credentials: 'include', 
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ texto: "¡Excelente tutorial!" })
})
.then(response => {
  if (response.ok) console.log("Comentario enviado con tu sesión activa");
});Lenguaje del código: JavaScript (javascript)

Explicación del ejemplo:

  • Al usar credentials: 'include', el navegador busca cualquier cookie guardada para api-tu-sitio.com y la adjunta automáticamente a la petición.
  • Nota importante para el servidor: Si usas 'include' en una petición a otro dominio, el servidor debe responder con la cabecera Access-Control-Allow-Credentials: true, de lo contrario, el navegador bloqueará la respuesta por seguridad.

8. El atributo cache.

Define cómo debe interactuar la petición con el sistema de almacenamiento temporal (caché) del navegador. Sirve para decidir si queremos usar una copia guardada de una respuesta anterior o si obligamos al navegador a realizar un viaje completo hasta el servidor.

8.1. Los valores más importantes de cache

  1. default (Valor por defecto):
    • El navegador decide según las reglas estándar de HTTP. Si el recurso no ha expirado, usa la caché; si es viejo, consulta al servidor.
  2. no-cache:
    • El navegador busca en la caché pero siempre consulta al servidor para ver si hay una versión más nueva antes de usarla.
  3. reload:
    • Ignora completamente la caché y obliga al navegador a pedir los datos directamente al servidor, actualizando la caché con la nueva respuesta.
  4. force-cache:
    • El navegador usa la copia de la caché sin importar qué tan vieja sea. Si no hay nada guardado, entonces va al servidor.
  5. only-if-cached:
    • El navegador solo devuelve datos si ya están en la caché. Si no están, lanza un error de red.

8.2. Ejemplo práctico

Imagina que estás enviando una petición para obtener una configuración que rara vez cambia. Queremos que, una vez obtenida, se use siempre la versión guardada para ganar velocidad.

fetch('https://api.tu-sitio.com/configuracion-app', {
  method: 'GET', // Usamos GET para este ejemplo de optimización
  cache: 'force-cache', // Obligamos al navegador a usar la copia guardada
})
.then(response => response.json())
.then(config => {
  console.log("Configuración cargada instantáneamente desde la caché:", config);
});Lenguaje del código: JavaScript (javascript)

Explicación:

  • Uso en envíos (POST): Aunque el envío de datos suele ser dinámico, a veces queremos que la respuesta de un envío exitoso se refresque inmediatamente en la memoria del navegador usando reload.
  • Al usar cache: 'force-cache', la segunda vez que el usuario entre a tu web, Fetch no hará ninguna llamada a internet; sacará los datos directamente del disco duro del usuario.

9. El atributo signal

El atributo signal es una propiedad que permite comunicar a una petición Fetch que debe detenerse o «abortarse» antes de que termine de forma natural. Para que funcione, necesita trabajar en conjunto con una interfaz de JavaScript llamada AbortController.

9.1. ¿Para qué sirve signal?

Su función principal es el control de flujo y limpieza. Existen dos casos críticos donde es obligatorio usarlo:

  1. Limpieza del DOM:
    • Si un usuario hace clic en un botón para cargar datos pero decide navegar a otra sección antes de que la petición termine, debemos cancelar la petición para no gastar recursos innecesarios.
  2. Timeouts (Tiempos de espera):
    • Si el servidor tarda demasiado (por ejemplo, más de 5 segundos), podemos usar signal para cancelar la petición automáticamente y mostrar un mensaje de «Tiempo de espera agotado».

9.2. ¿Cómo usarlo en 3 pasos?

  1. Crear el controlador: Instancias new AbortController().
  2. Vincular la señal: Pasas controller.signal al objeto de configuración de Fetch.
  3. Ejecutar la cancelación: Llamas a controller.abort() cuando decidas que la petición ya no es necesaria.

9.3. Ejemplo práctico:

Este es el uso más profesional en el mundo real. Si el servidor no responde en 3 segundos, cancelamos todo:

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 3000); // 3 segundos de límite

fetch('https://api.tu-sitio.com/datos-pesados', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ consulta: "procesar_todo" }),
  // Pasamos la señal al fetch
  signal: controller.signal 
})
.then(response => response.json())
.then(data => {
  clearTimeout(timeoutId); // Si llega a tiempo, cancelamos el temporizador
  console.log("Datos recibidos:", data);
})
.catch(error => {
  if (error.name === 'AbortError') {
    console.warn("¡Petición cancelada por tardar demasiado!");
  } else {
    console.error("Error de red:", error);
  }
});Lenguaje del código: JavaScript (javascript)

Explicación:

  • AbortController: Crea un «interruptor» para nuestra petición.
  • signal: Es el cable que conecta ese interruptor con el fetch.
  • controller.abort(): Al ejecutarse, Fetch lanza inmediatamente una excepción de tipo AbortError, la cual capturamos en el .catch() para informar al usuario con elegancia.

10. Manejo de respuestas del servidor

Aquí nos referimos al proceso de recibir, interpretar y verificar la respuesta que devuelve el servidor después de una petición fetch.

Cuando enviamos una petición, el servidor siempre responde con información. Esa respuesta puede indicar:

  • Que todo salió bien
  • Que hubo un error
  • Que los datos enviados no eran válidos
  • Que el recurso no existe

10.1. Flujo básico de una respuesta

  1. El navegador envía la petición con fetch
  2. El servidor responde
  3. fetch devuelve un objeto Response
  4. JavaScript debe leer y validar ese objeto

¿QUÉ ES EL objeto response?

Como dijimos, cuando haces un fetch, lo que recibes no son los datos directamente, sino un objeto llamado Response. Este objeto es como una caja cerrada que contiene el resultado del trámite, pero antes de usar lo que hay dentro, debemos revisar las etiquetas de seguridad.

¿QUÉ PASA UNA VEZ RECIBIDO EL objeto response?

Una vez que hemos validado la respuesta y extraído los datos con .json(), el siguiente paso es la manipulación del DOM. El navegador no muestra mensajes automáticamente; somos nosotros los que debemos «dibujar» el resultado.

¿Qué acciones prácticas realizamos en el DOM?

Dependiendo de lo que responda el servidor, estas son las acciones más comunes que deberías incluir en tu tutorial:

  1. Feedback Visual de Éxito o Error
    • Es la acción más básica. Usamos elementos HTML (como un <div> o <span>) para informar al usuario.
      • En la práctica: Cambiamos el texto con .textContent y el color con .style o .classList.
      • Ejemplo: «¡Registro exitoso!» en verde o «El correo ya existe» en rojo.
  2. Limpieza del Formulario
    • Si el envío fue exitoso, lo profesional es no dejar los datos escritos para evitar que el usuario los envíe dos veces por error.
      • En la práctica: Usamos el método formulario.reset() para vaciar todos los campos de un solo golpe.
  3. Estados de Carga (Loading)
    • Antes de recibir la respuesta (mientras el await está trabajando), debemos avisar que algo está pasando.
      • En la práctica: Deshabilitamos el botón de envío (button.disabled = true) y cambiamos su texto a «Enviando…». Esto evita peticiones duplicadas.
  4. Redirección o Navegación
    • A veces, tras enviar datos (como en un Login), no queremos mostrar un mensaje, sino llevar al usuario a otra parte.
      • En la práctica: Usamos window.location.href = '/dashboard' para mover al usuario a su panel de control una vez que el servidor confirma que sus credenciales son válidas.

10.2 Ejemplo práctico

Enviando un mensaje con la estructura .then()

Imagina que enviamos un simple mensaje de texto a una API. Observa cómo el objeto Response aparece en el primer .then():

fetch('https://api.ejemplo.com/enviar', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ mensaje: "Hola Mundo" })
})
// Aquí recibimos el objeto Response (lo llamamos 'res')
.then(res => {
  console.log("Objeto Response recibido:", res);
  
  if (res.ok) {
    return res.json(); // Abrimos la "caja" para sacar los datos
  }
  throw new Error("Algo salió mal");
})
// Aquí recibimos los DATOS ya procesados
.then(datos => {
  console.log("Datos finales:", datos);
})
.catch(error => console.error(error));Lenguaje del código: JavaScript (javascript)

¿QUÉ HACE EXACTAMENTE EL objeto Response?

El objeto Response no son los datos, sino el paquete que los contiene. Su función es darte información sobre el éxito del viaje antes de que gastes tiempo procesando el contenido.

Aquí tenés sus 3 funciones principales explicadas de forma concisa:

  1. Actúa como un Escudo de Validación (ok y status)
    • Antes de mostrar nada en tu web, el objeto Response te dice si la petición fue exitosa.
    • Para qué sirve: Con la propiedad res.ok (true/false) o res.status (ej. 200, 404), sabés si el servidor aceptó tus datos sin tener que leer todo el mensaje. Es como mirar el sello de una carta antes de abrirla.
  2. Contiene los Metadatos de la Respuesta (headers)
    • El objeto guarda información sobre la respuesta que no es el «mensaje» en sí.
    • Para qué sirve: Te permite saber, por ejemplo, en qué formato te está respondiendo el servidor (¿Es un JSON?, ¿Es una imagen?, ¿Es un PDF?) o si te está enviando una nueva cookie de sesión.
  3. Gestiona el Flujo de Datos (json(), text(), blob())
    • El contenido real (el cuerpo) llega como un «flujo» (stream). El objeto Response tiene los métodos necesarios para transformar ese flujo en algo que JavaScript entienda.
    • Para qué sirve: El método res.json() toma ese flujo y lo convierte en un objeto usable. Dato clave: Al hacer esto, el objeto «vacía» su contenido; por eso solo podés leer el cuerpo de una respuesta una sola vez.

11. Ejemplo real de envío de datos

Un caso muy común en proyectos web es un formulario de registro de usuario.

El usuario completa:

  • nombre
  • email
  • contraseña

Cuando hace clic en «Registrarse», JavaScript toma esos datos y los envía a una API para crear el nuevo usuario.

VAMOS CON EL CÓDIGO DEL EJEMPLO:

HTML del formulario

<form id="formRegistro">
  <input type="text" id="nombre" placeholder="Nombre" required>
  <input type="email" id="email" placeholder="Email" required>
  <input type="password" id="password" placeholder="Contraseña" required>

  <button type="submit">Registrarse</button>
</form>

<p id="mensaje"></p>
Lenguaje del código: HTML, XML (xml)

Código JavaScript con async/await

// Seleccionamos los elementos del DOM necesarios: el formulario para escuchar el evento y un div/p para mostrar mensajes al usuario.
const formulario = document.querySelector('#formRegistro');
const mensaje = document.querySelector('#mensaje');

// Escuchamos el evento 'submit'. Usamos 'async' porque dentro realizaremos una operación asíncrona (fetch).
formulario.addEventListener('submit', async (event) => {
  // Evitamos que la página se recargue, que es el comportamiento por defecto de los formularios.
  event.preventDefault();

  // Capturamos los valores actuales de los campos de entrada.
  const nombre = document.querySelector('#nombre').value;
  const email = document.querySelector('#email').value;
  const password = document.querySelector('#password').value;

  // Creamos un objeto que empaqueta la información del usuario de forma organizada.
  const datosUsuario = {
    nombre,
    email,
    password
  };

  try {
    /* CONFIGURACIÓN DEL FETCH:
       Aquí iniciamos la petición HTTP. Fetch por defecto es GET, por eso pasamos un segundo 
       argumento de configuración (un objeto {}) para personalizarlo:
    */
    const response = await fetch('https://api.ejemplo.com/usuarios', {
      // 1. Definimos el método POST porque estamos enviando (creando) datos en el servidor.
      method: 'POST',
      
      /* 2. Definimos los HEADERS (Cabeceras): 
            'Content-Type': Le dice al servidor que los datos que enviamos están en formato JSON.
            'Accept': Le dice al servidor que esperamos recibir una respuesta también en JSON.
      */
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      },

      /* 3. Definimos el BODY (Cuerpo): 
            Los objetos de JavaScript no se pueden enviar directamente por HTTP. 
            Usamos JSON.stringify() para convertir nuestro objeto 'datosUsuario' en una cadena de texto JSON.
      */
      body: JSON.stringify(datosUsuario)
    });

    // Validamos si la respuesta fue exitosa (status en el rango 200-299).
    // Fetch no lanza error automáticamente si el servidor responde con un 404 o 500, hay que checkearlo manualmente.
    if (!response.ok) {
      throw new Error(`Error HTTP: ${response.status}`);
    }

    /* PROCESAMIENTO DE LA RESPUESTA:
       await response.json() toma el cuerpo de la respuesta del servidor (que viene como stream)
       y lo parsea de nuevo a un objeto de JavaScript para que podamos usarlo.
    */
    const resultado = await response.json();

    // Si todo salió bien, actualizamos la interfaz con la información recibida (ej. un ID generado).
    mensaje.textContent = `Usuario creado correctamente. ID: ${resultado.id}`;
    console.log(resultado);

  } catch (error) {
    // Si ocurre un error de red o lanzamos el error manual en el 'if', el flujo cae aquí.
    mensaje.textContent = 'No fue posible registrar el usuario';
    console.error(error);
  }
});Lenguaje del código: JavaScript (javascript)

Explicación de la configuración de fetch

fetch('https://api.ejemplo.com/usuarios', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  },
  body: JSON.stringify(datosUsuario)
})
Lenguaje del código: JavaScript (javascript)

1. method: 'POST'

method: 'POST'Lenguaje del código: JavaScript (javascript)

Indica que la petición va a enviar datos nuevos al servidor.

En este ejemplo, el servidor entiende que debe crear un nuevo usuario.

2. headers

headers: {
  'Content-Type': 'application/json',
  'Accept': 'application/json'
}Lenguaje del código: JavaScript (javascript)

Content-Type

'Content-Type': 'application/json'Lenguaje del código: JavaScript (javascript)

Le informa al servidor que los datos enviados están en formato JSON.

Sin este header, muchos servidores no podrían interpretar correctamente el contenido del body.

Accept

'Accept': 'application/json'Lenguaje del código: JavaScript (javascript)

Indica que JavaScript espera recibir una respuesta JSON.

3. body

body: JSON.stringify(datosUsuario)Lenguaje del código: JavaScript (javascript)

body contiene los datos que se enviarán al servidor.

La variable datosUsuario contiene:

{
  nombre: 'Juan',
  email: '[email protected]',
  password: '123456'
}Lenguaje del código: CSS (css)

Pero fetch no puede enviar directamente un objeto JavaScript. Por eso se utiliza:

JSON.stringify(datosUsuario)Lenguaje del código: JavaScript (javascript)

Eso transforma el objeto en texto JSON:

{
  "nombre": "Juan",
  "email": "[email protected]",
  "password": "123456"
}Lenguaje del código: JSON / JSON con comentarios (json)

Resultado esperado del código

Si la petición funciona correctamente:

  • se crea el usuario en el servidor
  • el servidor devuelve información del nuevo usuario
  • se actualiza el DOM mostrando un mensaje

Resultado visible:

Usuario creado correctamente. ID: 15

Ejemplo de respuesta que devuelve la API

Supongamos que el servidor responde con este JSON:

{
  "id": 15,
  "nombre": "Juan",
  "email": "[email protected]",
  "creado": true
}Lenguaje del código: JSON / JSON con comentarios (json)

Ese JSON llega dentro del objeto response.

Cómo se maneja la respuesta (response)

const response = await fetch(...);Lenguaje del código: JavaScript (javascript)

La variable response no contiene directamente el JSON. Contiene un objeto especial con información sobre la respuesta.

Por ejemplo:

console.log(response);Lenguaje del código: JavaScript (javascript)

Podría mostrar algo parecido a:

Response {
  ok: true,
  status: 201,
  statusText: 'Created'
}Lenguaje del código: CSS (css)

Verificar si la respuesta fue correcta

if (!response.ok) {
  throw new Error(`Error HTTP: ${response.status}`);
}Lenguaje del código: JavaScript (javascript)
  • response.ok vale true si el código HTTP está entre 200 y 299
  • response.status contiene el código exacto

En este caso:

response.status // 201Lenguaje del código: JavaScript (javascript)

201 significa: “recurso creado correctamente”.

Convertir la respuesta a un objeto JavaScript

const resultado = await response.json();Lenguaje del código: JavaScript (javascript)

response.json() toma el JSON devuelto por el servidor y lo transforma en un objeto JavaScript:

{
  id: 15,
  nombre: 'Juan',
  email: '[email protected]',
  creado: true
}Lenguaje del código: CSS (css)

Ejemplo completo del flujo

  1. El usuario completa el formulario
  2. JavaScript obtiene los valores del DOM
  3. Se crea el objeto datosUsuario
  4. fetch envía los datos con POST
  5. El servidor crea el usuario
  6. El servidor responde con un JSON
  7. response.json() transforma esa respuesta
  8. JavaScript usa el resultado para actualizar el DOM
  9. Si ocurre un error, catch lo captura y muestra un mensaje

Ejemplo de error y respuesta del servidor

Si el email ya existe, la API podría devolver:

{
  "error": "El email ya está registrado"
}Lenguaje del código: JSON / JSON con comentarios (json)

Con código HTTP:

409 Conflict

Entonces:

if (!response.ok) {
  throw new Error(`Error HTTP: ${response.status}`);
}Lenguaje del código: JavaScript (javascript)

Generaría:

Error HTTP: 409Lenguaje del código: JavaScript (javascript)

Y el catch mostraría el mensaje de error en pantalla.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *