En este laboratorio, aprenderá las habilidades esenciales para interactuar con APIs web utilizando JavaScript moderno. Explorará la API fetch, una herramienta potente y flexible integrada en los navegadores web, para realizar solicitudes de red asíncronas. El objetivo principal es comprender cómo solicitar datos de un servidor, manejar la respuesta y gestionar posibles errores, formando una habilidad fundamental para construir aplicaciones web dinámicas.
Comenzará realizando una solicitud GET básica para recuperar datos y luego aprenderá a manejar la respuesta, analizarla como JSON y mostrar los datos obtenidos dentro de un elemento HTML. El laboratorio también cubre aspectos cruciales como la implementación de un manejo de errores robusto, la realización de solicitudes POST para enviar datos a un servidor y la autenticación de sus solicitudes utilizando una clave API (API key).
Este es un Guided Lab, que proporciona instrucciones paso a paso para ayudarte a aprender y practicar. Sigue las instrucciones cuidadosamente para completar cada paso y obtener experiencia práctica. Los datos históricos muestran que este es un laboratorio de nivel principiante con una tasa de finalización del 88%. Ha recibido una tasa de reseñas positivas del 100% por parte de los estudiantes.
Realizar una Solicitud GET Básica con la API fetch
En este paso, aprenderá a realizar una llamada API fundamental en JavaScript: una solicitud GET. Utilizaremos la API fetch, que es una herramienta moderna, potente y flexible integrada en todos los navegadores web modernos y disponible en entornos Node.js 18+. Nos permite solicitar recursos de un servidor de forma asíncrona.
La función fetch se basa en promesas (promise-based), lo que significa que devuelve una Promise que se resuelve con la Response de esa solicitud, ya sea exitosa o no. Esto nos permite manejar el resultado de la operación asíncrona cuando se completa.
Primero, creemos un archivo para escribir nuestro código. En el explorador de archivos en el lado izquierdo de su pantalla, cree un nuevo archivo llamado index.js en el directorio ~/project.
Utilizaremos la API JSONPlaceholder para este ejemplo. Es una API REST en línea gratuita y falsa, perfecta para pruebas y prototipos. Solicitaremos un único elemento "todo".
Ahora, agregue el siguiente código a su archivo index.js. Este código define la URL del endpoint de la API y utiliza fetch para realizar una solicitud GET.
// Define la URL de la API para un solo elemento todo
const apiUrl = "https://jsonplaceholder.typicode.com/todos/1";
// Realiza una solicitud GET utilizando la API fetch
fetch(apiUrl)
.then((response) => {
// La función fetch devuelve una promesa.
// El primer bloque .then() recibe el objeto Response.
// Necesitamos llamar al método .json() en la respuesta para analizar el cuerpo del texto como JSON.
return response.json();
})
.then((data) => {
// El segundo bloque .then() recibe los datos JSON analizados.
console.log("Datos obtenidos exitosamente:");
console.log(data);
})
.catch((error) => {
// El bloque .catch() se ejecutará si ocurre algún error durante la operación fetch.
console.error("Error al obtener datos:", error);
});
Analicemos el código:
apiUrl: Almacenamos la URL del endpoint de la API con el que queremos contactar en una constante.
fetch(apiUrl): Esto inicia la solicitud GET a la URL especificada y devuelve una Promise.
.then(response => response.json()): Cuando la Promise se resuelve, se llama a esta función. El objeto response no son los datos JSON reales, sino una representación de toda la respuesta HTTP. Llamamos al método response.json() para extraer el contenido del cuerpo JSON, que a su vez devuelve otra Promise.
.then(data => { ... }): Este segundo .then() maneja la Promise devuelta por response.json(). El parámetro data ahora contiene el objeto JSON real de la API. Registramos estos datos en la consola.
.catch(error => { ... }): Si la Promise es rechazada en algún momento (por ejemplo, debido a un error de red), este bloque capturará el error y lo registrará en la consola.
Para ejecutar su script y ver el resultado, abra una nueva terminal en el WebIDE y ejecute el siguiente comando:
node ~/project/index.js
Debería ver los datos obtenidos impresos en su consola, que representan un único elemento "todo" de la API.
Salida esperada:
Data fetched successfully:
{ userId: 1, id: 1, title: 'delectus aut autem', completed: false }
Manejar la Respuesta y Analizar Datos JSON
En este paso, profundizaremos en el manejo de la respuesta de una llamada API. Cuando utiliza fetch, el servidor responde con un objeto Response. Exploraremos qué contiene este objeto y cómo analizar correctamente los datos JSON de su cuerpo para utilizarlos en su aplicación.
El objeto Response que recibe en el primer bloque .then() no son los datos en sí. Es una representación de toda la respuesta HTTP, incluyendo códigos de estado (como 200 para OK), encabezados (headers) y el cuerpo de la respuesta (response body). El cuerpo es un flujo de datos, y para utilizarlo, necesita leerlo.
La API fetch proporciona varios métodos para esto, como .text() para texto plano y .json() para analizar datos JSON. El método .json() lee el flujo de respuesta hasta completarse y devuelve una nueva promesa que se resuelve con el resultado de analizar el texto del cuerpo como un objeto de JavaScript. Es por eso que encadenamos un segundo .then() para trabajar con los datos reales.
Modifiquemos nuestro archivo ~/project/index.js para demostrar esto. En lugar de simplemente registrar todo el objeto de datos, accederemos y registraremos propiedades específicas de él, mostrando que tenemos un objeto de JavaScript normal con el que trabajar.
Actualice el contenido de su archivo ~/project/index.js con el siguiente código:
// Define la URL de la API para un solo elemento todo
const apiUrl = "https://jsonplaceholder.typicode.com/todos/1";
fetch(apiUrl)
.then((response) => {
// El método response.json() analiza el cuerpo JSON de la respuesta
// y devuelve una promesa que se resuelve con el objeto JavaScript resultante.
return response.json();
})
.then((data) => {
// Ahora 'data' es un objeto JavaScript. Podemos acceder a sus propiedades.
console.log("Datos JSON analizados exitosamente:");
console.log(`Título del Todo: ${data.title}`);
console.log(`¿Está Completado?: ${data.completed}`);
})
.catch((error) => {
// El bloque .catch() se ejecutará si ocurre algún error durante la operación fetch.
console.error("Error al obtener o analizar datos:", error);
});
En este código actualizado, el segundo bloque .then() ahora recibe los data como un objeto JavaScript. Utilizamos literales de plantilla (la sintaxis de comillas invertidas `) para crear cadenas que incluyen los valores de data.title y data.completed, demostrando que el JSON se ha analizado correctamente.
Ahora, ejecute el script nuevamente en su terminal para ver la nueva salida:
node ~/project/index.js
Verá una salida más estructurada, confirmando que ha accedido con éxito a las propiedades del objeto JSON analizado.
Salida esperada:
Successfully parsed JSON data:
Todo Title: delectus aut autem
Is Completed: false
Mostrar Datos Obtenidos en un Elemento HTML
En este paso, aprenderá a tomar los datos obtenidos de una llamada API y mostrarlos en una página web. Si bien registrar datos en la consola es útil para el desarrollo, el objetivo final suele ser presentar esta información al usuario. Esto requiere un archivo HTML para estructurar el contenido y JavaScript para manipular el Modelo de Objetos del Documento (DOM).
Primero, necesitamos un archivo HTML. En el explorador de archivos de la izquierda, cree un nuevo archivo llamado index.html en el directorio ~/project.
Agregue la siguiente estructura HTML básica a su archivo index.html. Este archivo incluye un encabezado y un elemento div con el ID data-output, que servirá como contenedor para nuestros datos obtenidos. La etiqueta <script> al final del cuerpo asegura que nuestro JavaScript se ejecute después de que los elementos HTML se hayan cargado.
A continuación, necesita modificar su archivo ~/project/index.js para interactuar con este HTML. En lugar de registrar en la consola, el script encontrará el elemento div por su ID y actualizará su contenido con los datos de la API.
Reemplace el contenido de ~/project/index.js con el siguiente código:
const apiUrl = "https://jsonplaceholder.typicode.com/todos/1";
// Selecciona el elemento HTML donde mostraremos los datos
const outputElement = document.getElementById("data-output");
fetch(apiUrl)
.then((response) => response.json())
.then((data) => {
// Una vez que tengamos los datos, actualizamos el contenido HTML.
// Usamos innerHTML para reemplazar el mensaje "Loading..." con datos estructurados.
outputElement.innerHTML = `
<p><strong>Title:</strong> ${data.title}</p>
<p><strong>Completed:</strong> ${data.completed}</p>
`;
})
.catch((error) => {
// Si ocurre un error, mostramos un mensaje de error al usuario.
outputElement.textContent = "Failed to load data.";
console.error("Error fetching data:", error);
});
Ahora, para ver el resultado, necesita servir estos archivos a través de un servidor web. El entorno LabEx incluye Python, que tiene un servidor web simple incorporado.
Abra una nueva terminal en el WebIDE.
Inicie el servidor web ejecutando el siguiente comando. Esto servirá los archivos en el directorio actual (~/project) en el puerto 8080.
python3 -m http.server 8080
La plataforma LabEx detectará este servicio en ejecución y proporcionará una pestaña "Web 8080" para previsualizar su página index.html.
Ahora debería ver su página web mostrando el título y el estado de finalización del elemento "todo" obtenido, reemplazando el mensaje inicial "Loading data...".
Implementar Manejo de Errores para Llamadas a la API
En este paso, nos centraremos en hacer que nuestra llamada API sea más robusta implementando un manejo de errores adecuado. Las solicitudes API pueden fallar por diversas razones, como una URL incorrecta, problemas de red o problemas del lado del servidor. Es crucial manejar estas posibles fallas de manera elegante para proporcionar una mejor experiencia de usuario.
Un detalle clave sobre la API fetch es que su promesa no se rechaza ante estados de error HTTP como 404 (Not Found) o 500 (Internal Server Error). Solo se rechaza si hay un fallo de red que impida que la solicitud se complete. Para manejar errores HTTP, necesitamos verificar la propiedad response.ok, que es true para respuestas exitosas (códigos de estado 200-299).
Actualicemos nuestro ~/project/index.js para verificar el estado de la respuesta y manejar posibles errores.
Reemplace el contenido de su archivo ~/project/index.js con el siguiente código. Estamos agregando una verificación dentro del primer bloque .then().
const apiUrl = "https://jsonplaceholder.typicode.com/todos/1";
const outputElement = document.getElementById("data-output");
fetch(apiUrl)
.then((response) => {
// Verifica si la respuesta fue exitosa.
// La propiedad 'ok' es un booleano que es true si el código de estado está en el rango 200-299.
if (!response.ok) {
// Si no lo es, lanzamos un error que será capturado por el bloque .catch().
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then((data) => {
outputElement.innerHTML = `
<p><strong>Title:</strong> ${data.title}</p>
<p><strong>Completed:</strong> ${data.completed}</p>
`;
})
.catch((error) => {
// Muestra un mensaje de error amigable para el usuario en el elemento HTML.
outputElement.textContent = "Failed to load data. Please try again later.";
// Registra el error técnico en la consola para fines de depuración.
console.error("Error fetching data:", error);
});
Para ver nuestro manejo de errores en acción, usemos intencionalmente una URL de API inválida. Modifique apiUrl en ~/project/index.js para que apunte a un recurso que no existe. Esto hará que la API devuelva un error 404 Not Found.
Cambie esta línea en su archivo index.js: const apiUrl = 'https://jsonplaceholder.typicode.com/todos/1';
A esto: const apiUrl = 'https://jsonplaceholder.typicode.com/invalid-path/1';
Ahora, veamos el resultado. Si su servidor web Python del paso anterior todavía se está ejecutando, simplemente actualice la pestaña de vista previa en su navegador. Si lo detuvo, inícielo nuevamente desde la terminal:
python3 -m http.server 8080
Luego, abra la vista previa. En lugar de los datos, debería ver el mensaje de error mostrado en la página porque nuestra verificación if (!response.ok) capturó el error 404.
Salida esperada en la página web:
Failed to load data. Please try again later.
Importante: Antes de pasar al siguiente paso, recuerde cambiar apiUrl de nuevo a la correcta: https://jsonplaceholder.typicode.com/todos/1.
Realizar una Solicitud POST para Enviar Datos
En este paso, aprenderá a enviar datos a un servidor utilizando una solicitud POST. Hasta ahora, solo hemos obtenido (o "GET") datos. Una solicitud POST se utiliza para enviar datos a un recurso especificado, a menudo provocando un cambio de estado o la creación de una nueva entrada en el servidor.
Para hacer esto, necesitamos proporcionar más información a la función fetch, incluido el método de solicitud, las cabeceras (headers) para describir los datos que estamos enviando y los datos en sí en el cuerpo de la solicitud (request body).
Primero, actualicemos nuestro archivo ~/project/index.html para incluir un formulario. Esto nos permitirá ingresar datos que luego podremos enviar a la API. Reemplace todo el contenido de index.html con lo siguiente:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>POST Request Example</title>
</head>
<body>
<h1>Create a New Todo Item</h1>
<form id="add-todo-form">
<input
type="text"
id="todo-title"
placeholder="Enter a new todo title"
required
/>
<button type="submit">Add Todo</button>
</form>
<hr />
<h2>Server Response:</h2>
<div id="response-output"></div>
<script src="index.js"></script>
</body>
</html>
Este HTML crea un formulario simple con un campo de entrada de texto y un botón de envío, además de un div para mostrar la respuesta del servidor.
A continuación, reemplazaremos completamente el código en ~/project/index.js para manejar el envío del formulario y realizar una solicitud POST.
Reemplace el contenido de su archivo ~/project/index.js con el siguiente código:
// El endpoint de la API para crear nuevos todos
const apiUrl = "https://jsonplaceholder.typicode.com/todos";
// Obtener el formulario y el elemento de salida de respuesta del DOM
const todoForm = document.getElementById("add-todo-form");
const responseOutput = document.getElementById("response-output");
// Agregar un listener de eventos para el evento submit del formulario
todoForm.addEventListener("submit", function (event) {
// Prevenir el comportamiento de envío de formulario por defecto
event.preventDefault();
// Obtener el título del campo de entrada
const todoTitle = document.getElementById("todo-title").value;
// Los datos que queremos enviar en la solicitud POST
const newTodo = {
title: todoTitle,
completed: false,
userId: 1
};
// Las opciones para la solicitud fetch
const requestOptions = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(newTodo)
};
// Realizar la solicitud POST
fetch(apiUrl, requestOptions)
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// La API devuelve el objeto creado, incluido un nuevo 'id'
return response.json();
})
.then((data) => {
// Mostrar la respuesta del servidor en nuestro div de salida
responseOutput.innerHTML = `<p>Successfully created todo!</p><pre>${JSON.stringify(data, null, 2)}</pre>`;
})
.catch((error) => {
responseOutput.textContent = "Failed to create todo.";
console.error("Error:", error);
});
});
Analicemos el nuevo objeto requestOptions:
method: 'POST': Esto le dice a fetch que realice una solicitud POST.
headers: { 'Content-Type': 'application/json' }: Esta cabecera informa al servidor que los datos en el cuerpo están en formato JSON.
body: JSON.stringify(newTodo): Estos son los datos reales que estamos enviando. Deben convertirse en una cadena JSON antes de ser enviados.
Ahora, inicie su servidor web nuevamente si no está en ejecución:
python3 -m http.server 8080
Abra la vista previa, escriba un título para un nuevo elemento de tarea en el campo de entrada y haga clic en el botón "Add Todo". Debería ver un mensaje de éxito y los datos devueltos por el servidor, que incluyen el nuevo id para el elemento que "creó".
Autenticar Solicitudes con una Clave API
En este paso, aprenderá a autenticar sus solicitudes API utilizando una clave API (API key). Muchas APIs requieren autenticación para identificar al usuario, controlar el acceso a los datos y rastrear el uso. Una clave API es una cadena única de caracteres que incluye en su solicitud para demostrar que tiene permiso para usar la API.
Existen varias formas de enviar una clave API, pero un método común y seguro es incluirla en las cabeceras de la solicitud (request headers), típicamente usando la cabecera Authorization.
Para este ejemplo, simularemos la obtención de datos de usuario protegidos que requieren una clave API. Modificaremos nuestro código para incluir una cabecera Authorization con un token "Bearer", que es una forma estándar de enviar credenciales de autenticación.
Primero, simplifiquemos nuestro archivo ~/project/index.html para que solo muestre los datos del usuario obtenidos. Reemplace su contenido con lo siguiente:
A continuación, reemplace el contenido de ~/project/index.js con el código que se muestra a continuación. Este script realizará una solicitud GET para obtener los datos de un usuario e incluirá una clave API ficticia en las cabeceras.
// Define la URL de la API para el perfil de un usuario
const apiUrl = "https://jsonplaceholder.typicode.com/users/1";
// En una aplicación real, obtendrías esta clave de tu proveedor de API.
const apiKey = "YOUR_SECRET_API_KEY_HERE";
// Selecciona el elemento HTML para la salida
const userProfileElement = document.getElementById("user-profile");
// Crea el objeto de opciones de solicitud, incluyendo las cabeceras para la autenticación
const requestOptions = {
method: "GET",
headers: {
Authorization: `Bearer ${apiKey}`
}
};
// Realiza la solicitud GET autenticada
fetch(apiUrl, requestOptions)
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then((data) => {
// Muestra los datos del usuario obtenidos
userProfileElement.innerHTML = `
<p><strong>Name:</strong> ${data.name}</p>
<p><strong>Email:</strong> ${data.email}</p>
<p><strong>Website:</strong> ${data.website}</p>
`;
})
.catch((error) => {
userProfileElement.textContent = "Failed to load user profile.";
console.error("Error:", error);
});
En este código:
Definimos una apiKey de marcador de posición.
Creamos un objeto requestOptions.
Dentro de headers, agregamos una clave Authorization. El valor Bearer ${apiKey} es un formato común, donde "Bearer" es el tipo de token, seguido de la clave en sí.
Nota: La API JSONPlaceholder que estamos utilizando es pública y en realidad no requiere una clave API. Simplemente ignorará esta cabecera. Sin embargo, este código demuestra el método estándar que usaría para las muchas APIs del mundo real que sí requieren autenticación.
Para ver el resultado, inicie su servidor web si aún no está en ejecución:
python3 -m http.server 8080
Luego, abra la vista previa. La página se cargará correctamente y mostrará el perfil del usuario, demostrando que ha estructurado correctamente una solicitud API autenticada.
Resumen
En este laboratorio, aprendió a llamar a una API en JavaScript utilizando la API moderna fetch. Comenzó realizando una solicitud GET básica para recuperar datos de un endpoint de API público. Practicó el manejo de la naturaleza asíncrona de fetch utilizando promesas, encadenando bloques .then() para procesar la respuesta y analizar el texto del cuerpo como JSON. Las habilidades clave incluyeron mostrar los datos obtenidos dinámicamente dentro de un elemento HTML e implementar un manejo de errores robusto con un bloque .catch() para gestionar posibles fallos de red.
Basándose en estos fundamentos, exploró cómo enviar datos a un servidor construyendo y ejecutando una solicitud POST, incluyendo las cabeceras necesarias y una carga útil JSON. Finalmente, aprendió un método común para asegurar las comunicaciones de la API autenticando sus solicitudes, lo que implicó incluir una clave API para obtener acceso autorizado a recursos protegidos.