Introducción: La Comunicación con el Mundo Externo
En el desarrollo de aplicaciones móviles modernas, pocas cosas son tan fundamentales como la capacidad de comunicarse con servidores externos. Las aplicaciones que construimos rara vez son islas autosuficientes; por el contrario, son clientes que consumen, manipulan y presentan datos provenientes de diversas fuentes en la web. Ya sea para mostrar el pronóstico del tiempo, listar productos de un e-commerce, cargar el feed de una red social o sincronizar tus archivos en la nube, la aplicación necesita solicitar datos a un servidor y, a menudo, también enviarle información. Este intercambio es el latido de la mayoría de las apps.
En React Native, esta comunicación se realiza principalmente a través de APIs REST (o RESTful), un estilo arquitectónico que utiliza los verbos estándar del protocolo HTTP (GET, POST, PUT, DELETE) para operar sobre recursos identificados por URLs. Para realizar estas peticiones HTTP, los desarrolladores de JavaScript disponemos de herramientas nativas y de librerías de terceros. En esta lección, nos sumergiremos en las dos más populares y esenciales: la API nativa Fetch y la librería Axios. Comprender sus fortalezas, diferencias y casos de uso óptimos es un paso crucial para pasar de construir interfaces estáticas a desarrollar aplicaciones dinámicas, conectadas y realmente útiles.
El objetivo de esta lección es equiparte con el conocimiento práctico y sólido para elegir e implementar la solución correcta para consumir APIs en tus proyectos con React Native y Expo. Abordaremos desde la petición más básica hasta la gestión avanzada de errores, autenticación y optimización, siempre con ejemplos reales y funcionales que podrás integrar directamente en tu código.
Concepto Clave: HTTP, Clientes y Servidores
Imagina que entras a un restaurante (tu aplicación React Native). Tú eres el cliente. Para obtener tu comida (los datos), necesitas comunicarte con el camarero. En este mundo digital, el camarero es la API (Application Programming Interface) del servidor. Tú, como cliente, realizas una solicitud (request): "Quisiera el menú del día, por favor" (esto sería un GET a la URL `/api/menu`). El camarero (la API) recibe tu pedido, va a la cocina (la base de datos o lógica del servidor), prepara lo solicitado y te trae una respuesta (response): el menú del día en una bandeja (usualmente en formato JSON). Si pides algo que no existe, el camarero te responde con un error: "Ese plato no lo tenemos" (código de estado 404).
HTTP es el protocolo de comunicación que define el lenguaje y las reglas de esta interacción. Los verbos HTTP (GET, POST, PUT, PATCH, DELETE) indican la intención de tu solicitud: obtener datos, crearlos, actualizarlos o eliminarlos. Los códigos de estado (200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 500 Internal Server Error) son la forma rápida y estandarizada en que el servidor te informa si tu solicitud fue exitosa, falló por tu culpa o falló por su culpa. Fetch y Axios son, en esta analogía, las diferentes formas en que puedes llamar al camarero: Fetch es como levantar la mano y hablar de manera directa (es nativo, viene integrado), mientras que Axios es como tener un dispositivo de mesa que te sugiere platos, recuerda tus alergias y maneja automáticamente ciertas formalidades (es una librería externa con características de conveniencia).
Cómo Funciona en la Práctica: El Flujo de una Petición
Independientemente de si usas Fetch o Axios, el flujo mental y de código para consumir una API sigue un patrón común que debes internalizar. Primero, necesitas conocer el endpoint, que es la URL específica del recurso con el que quieres interactuar (ej: `https://api.midominio.com/v1/usuarios`). Luego, construyes el objeto de configuración de la petición, especificando el método HTTP (GET, POST, etc.), los headers (como `Content-Type: application/json` o tokens de autorización `Authorization: Bearer TU_TOKEN`) y, para métodos como POST o PUT, el cuerpo (body) de la solicitud, que contiene los datos a enviar, típicamente en formato JSON.
Una vez lanzada la petición, esta opera de manera asíncrona. Esto es crucial en React Native, ya que la interfaz de usuario no debe congelarse esperando una respuesta del servidor, que puede tardar milisegundos o varios segundos. Tu código "espera" la respuesta de forma no bloqueante usando Promesas (Promises) y las palabras clave async/await. Cuando la respuesta llega, primero debes verificar si fue exitosa (código de estado 2xx). Luego, procesas el cuerpo de la respuesta, que normalmente necesitas convertir de texto JSON a un objeto JavaScript usable mediante `.json()`. Finalmente, actualizas el estado de tu componente React (con `useState` y `setState`) con los datos recibidos, lo que provocará un re-renderizado de la UI. Todo este proceso debe estar envuelto en un manejo robusto de errores (`try...catch`) para capturar fallos de red, respuestas de error del servidor y otros problemas imprevistos.
Tip de Expo: Expo y React Native tienen soporte nativo para Fetch y, por extensión, para Axios. No necesitas instalar paquetes adicionales para Fetch. Para Axios, sí debes instalarlo con `npx expo install axios`. Recuerda que las peticiones a Internet requieren permisos. En el archivo `app.json` o `app.config.js`, asegúrate de tener la lista de permisios `"ios": { "infoPlist": { "NSAppTransportSecurity": { "NSAllowsArbitraryLoads": true } } }` para desarrollo (iOS) y ser consciente de la Configuración de Seguridad de la Red (Android). Para producción, configura dominios específicos.
Código en Acción: Fetch vs. Axios, Lado a Lado
La mejor manera de entender las diferencias es viendo código real. Vamos a realizar una petición GET para obtener una lista de publicaciones y una petición POST para crear una nueva, utilizando ambas tecnologías. Observa la sintaxis y las diferencias en el manejo de respuestas y errores.
Ejemplo 1: Obteniendo Datos (GET)
// ==============================
// USANDO FETCH (API Nativa)
// ==============================
import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
const PostsScreenWithFetch = () => {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchPosts();
}, []);
const fetchPosts = async () => {
try {
setLoading(true);
// 1. Realizar la petición. Fetch devuelve una Promesa.
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
// 2. Verificar si la respuesta es OK (status 200-299).
if (!response.ok) {
// Si no es OK, lanzamos un error con el status.
throw new Error(`Error HTTP! estado: ${response.status}`);
}
// 3. Parsear el cuerpo de la respuesta de JSON a objeto JS.
const data = await response.json();
// 4. Actualizar el estado con los datos.
setPosts(data);
setError(null);
} catch (err) {
// 5. Capturar cualquier error (red, parsing, etc.)
setError(err.message);
console.error('Error al cargar posts:', err);
} finally {
// 6. Finalizar el estado de carga, ocurra éxito o error.
setLoading(false);
}
};
if (loading) return ;
if (error) return Error: {error};
return (
item.id.toString()}
renderItem={({ item }) => (
{item.title}
{item.body}
)}
/>
);
};
// ==============================
// USANDO AXIOS (Librería)
// ==============================
// Primero instalar: npx expo install axios
import axios from 'axios';
import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
const PostsScreenWithAxios = () => {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchPosts();
}, []);
const fetchPosts = async () => {
try {
setLoading(true);
// 1. Realizar la petición. Axios devuelve una respuesta donde los datos ya están en `data`.
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
// 2. Con Axios, los errores HTTP (4xx, 5xx) AUTOMÁTICAMENTE lanzan una excepción.
// No necesitamos verificar `response.ok`. Si la línea anterior no lanza error, fue exitosa.
// 3. Los datos ya están parseados en `response.data`.
setPosts(response.data);
setError(null);
} catch (err) {
// 4. Capturar error. Axios envuelve el error con mucha información útil.
setError(err.message);
// Podemos acceder a más detalles:
console.error('Error Axios:', err.response?.status, err.response?.data);
} finally {
setLoading(false);
}
};
// El JSX de renderizado es idéntico al ejemplo de Fetch...
if (loading) return ;
if (error) return Error: {error};
return (
item.id.toString()}
renderItem={({ item }) => (
{item.title}
{item.body}
)}
/>
);
};
Ejemplo 2: Enviando Datos (POST con Headers)
// ==============================
// CREAR UN POST CON FETCH
// ==============================
const createPostWithFetch = async (postData) => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Ejemplo de header de autorización:
// 'Authorization': `Bearer ${userToken}`,
},
body: JSON.stringify(postData), // ¡Debes convertir el objeto a string JSON!
});
if (!response.ok) {
throw new Error(`Error en creación: ${response.status}`);
}
const newPost = await response.json();
console.log('Post creado con Fetch:', newPost);
return newPost;
} catch (error) {
console.error('Fetch POST error:', error);
throw error;
}
};
// ==============================
// CREAR UN POST CON AXIOS
// ==============================
const createPostWithAxios = async (postData) => {
try {
const response = await axios.post(
'https://jsonplaceholder.typicode.com/posts',
postData, // ¡Axios convierte automáticamente el objeto a JSON!
{
headers: {
'Content-Type': 'application/json',
// 'Authorization': `Bearer ${userToken}`,
}
}
);
// La respuesta exitosa ya está aquí
console.log('Post creado con Axios:', response.data);
return response.data;
} catch (error) {
console.error('Axios POST error:', error.response?.data || error.message);
throw error;
}
};
// Uso de cualquiera de las funciones:
// const datos = { title: 'Mi título', body: 'Mi contenido', userId: 1 };
// await createPostWithFetch(datos);
// await createPostWithAxios(datos);
Errores Comunes y Cómo Evitarlos
Al comenzar con las peticiones HTTP, es normal tropezar con los mismos obstáculos. Reconocerlos te ahorrará horas de depuración.
1. Olvidar el manejo asíncrono (Async/Await o .then()): Llamar a una función que devuelve una Promesa sin `await` o sin `.then()` hará que tu código continúe ejecutándose antes de recibir la respuesta, llevando a estados `undefined` o `null`. Solución: Asegúrate de que toda función que realiza una petición sea `async` y de que uses `await` al llamarla, o encadena correctamente con `.then()` y `.catch()`.
2. No verificar el estado de la respuesta (especialmente con Fetch): Asumir que `fetch()` siempre tiene éxito es un error grave. Una respuesta 404 o 500 no lanza una excepción por sí sola en Fetch; solo lo hace un fallo de red. Solución: Siempre verifica la propiedad `response.ok` o el `response.status` dentro de un bloque `try` antes de intentar parsear el JSON.
3. Enviar el body sin stringificar (en Fetch) o sin el Content-Type correcto: En Fetch, el cuerpo de una petición POST debe ser una cadena de texto. Si envías un objeto JavaScript directamente, fallará silenciosamente. Solución: Usa siempre `JSON.stringify(datos)` para el `body` en Fetch y establece el header `'Content-Type': 'application/json'`. Axios lo hace automáticamente.
4. No limpiar peticiones en componentes desmontados (Memory Leaks): Si un componente se desmonta (el usuario navega a otra pantalla) mientras una petición está en vuelo, y luego esa petición completa e intenta actualizar el estado (`setState`), React arrojará un warning porque estás actualizando un componente que ya no existe. Solución: Usa un controller con Fetch o un cancel token con Axios para cancelar la petición en la función de limpieza del `useEffect`. Con Fetch moderno:
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
const fetchData = async () => {
try {
const response = await fetch(url, { signal });
// ... procesar respuesta
} catch (err) {
if (err.name === 'AbortError') {
console.log('Petición cancelada');
} else {
// Manejar otros errores
}
}
};
fetchData();
// Función de limpieza
return () => {
controller.abort();
};
}, [url]);
5. Manejo de errores genérico o inexistente: Un solo bloque `catch` que solo imprime en consola deja a tu usuario con una interfaz colgada o un comportamiento inesperado. Solución: Implementa un manejo de errores estratificado. Distingue entre errores de red, errores del servidor (4xx, 5xx) y errores de lógica de tu aplicación. Actualiza el estado de la UI para informar al usuario de manera amigable ("No hay conexión", "Credenciales incorrectas", "El servidor no responde").
Checklist de Dominio
Antes de considerar que dominas el consumo de APIs en React Native, asegúrate de poder verificar positivamente los siguientes puntos:
- Puedo explicar la diferencia entre una solicitud HTTP síncrona y asíncrona y por qué la asincronía es vital en las apps móviles.
- He implementado correctamente peticiones GET para obtener y mostrar listas de datos en un componente, manejando estados de carga y error en la UI.
- He implementado peticiones POST (o PUT/PATCH) para enviar datos a un servidor, configurando correctamente el método, headers (especialmente Content-Type) y body.
- Sé cómo agregar un token de autenticación (ej: Bearer Token) en el header `Authorization` de mis peticiones para acceder a endpoints protegidos.
- Puedo decidir cuándo usar Fetch (proyectos simples, sin querer dependencias extra) y cuándo preferir Axios (necesidad de interceptores, cancelación fácil, transformaciones automáticas).
- He implementado la cancelación de peticiones para prevenir fugas de memoria cuando un componente React se desmonta antes de que una petición finalice.
- Puedo leer e interpretar los códigos de estado HTTP comunes (200, 201, 400, 401, 403, 404, 500) y reaccionar apropiadamente en mi código.
- He estructurado mi código de peticiones HTTP de manera modular, posiblemente creando un archivo de servicio o cliente API reutilizable, en lugar de escribir `fetch` o `axios` directamente en cada componente.
Consejo Final: La práctica es fundamental. Te recomiendo crear una pequeña aplicación de práctica que consuma una API pública gratuita (como JSONPlaceholder, The Cat API, o la API de SpaceX). Experimenta con todos los métodos HTTP, maneja los errores, implementa un "pull to refresh" y juega con la cancelación. Esta experiencia práctica solidificará todos los conceptos teóricos que hemos cubierto aquí.