Skip to content

10. Consumo de APIs

🌐 10.1 Introducción al consumo de APIs

Section titled “🌐 10.1 Introducción al consumo de APIs”

Una API (Application Programming Interface) es un conjunto de endpoints que permiten a tu aplicación comunicarse con un servidor para obtener o enviar datos.

Método HTTPPropósitoEjemplo
GETObtener datosListar usuarios
POSTCrear datosRegistrar usuario
PUTActualizar todoEditar perfil completo
PATCHActualizar parcialCambiar solo email
DELETEEliminar datosBorrar usuario
Flujo típico
// 1. Componente se monta
// 2. useEffect hace petición a la API
// 3. Mientras carga, mostrar "Cargando..."
// 4. Si hay error, mostrar mensaje de error
// 5. Si hay datos, renderizar la lista
function ListaUsuarios() {
const [usuarios, setUsuarios] = useState([]);
const [cargando, setCargando] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// Petición a la API
}, []);
if (cargando) return <p>Cargando...</p>;
if (error) return <p>Error: {error}</p>;
return <ul>{/* renderizar usuarios */}</ul>;
}

GET con fetch
import { useState, useEffect } from 'react';
function ListaUsuarios() {
const [usuarios, setUsuarios] = useState([]);
const [cargando, setCargando] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => {
if (!response.ok) {
throw new Error('Error en la petición');
}
return response.json();
})
.then(data => {
setUsuarios(data);
setCargando(false);
})
.catch(err => {
setError(err.message);
setCargando(false);
});
}, []);
if (cargando) return <p>Cargando usuarios...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{usuarios.map(usuario => (
<li key={usuario.id}>{usuario.name}</li>
))}
</ul>
);
}
GET con async/await
import { useState, useEffect } from 'react';
function ListaUsuarios() {
const [usuarios, setUsuarios] = useState([]);
const [cargando, setCargando] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUsuarios = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) {
throw new Error('Error en la petición');
}
const data = await response.json();
setUsuarios(data);
} catch (err) {
setError(err.message);
} finally {
setCargando(false);
}
};
fetchUsuarios();
}, []);
// ... resto del componente
}
POST con fetch
function CrearUsuario() {
const [nombre, setNombre] = useState('');
const [enviando, setEnviando] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setEnviando(true);
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: nombre,
email: 'test@example.com'
}),
});
if (!response.ok) {
throw new Error('Error al crear usuario');
}
const nuevoUsuario = await response.json();
console.log('Usuario creado:', nuevoUsuario);
setNombre('');
} catch (err) {
console.error(err);
} finally {
setEnviando(false);
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={nombre}
onChange={e => setNombre(e.target.value)}
placeholder="Nombre"
/>
<button type="submit" disabled={enviando}>
{enviando ? 'Creando...' : 'Crear Usuario'}
</button>
</form>
);
}

Instalar Axios
npm install axios
GET con Axios
import { useState, useEffect } from 'react';
import axios from 'axios';
function ListaUsuarios() {
const [usuarios, setUsuarios] = useState([]);
const [cargando, setCargando] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/users')
.then(response => {
setUsuarios(response.data); // Axios ya parsea JSON
setCargando(false);
})
.catch(err => {
setError(err.message);
setCargando(false);
});
}, []);
// ... resto del componente
}
POST con Axios
import axios from 'axios';
const crearUsuario = async (datos) => {
try {
const response = await axios.post(
'https://jsonplaceholder.typicode.com/users',
datos // Axios convierte a JSON automáticamente
);
console.log('Usuario creado:', response.data);
return response.data;
} catch (err) {
console.error('Error:', err.response?.data || err.message);
throw err;
}
};
// Uso
crearUsuario({ name: 'Juan', email: 'juan@mail.com' });
CaracterísticafetchAxios
InstalaciónNativonpm install
Parseo JSONManual (.json())Automático
Manejo de erroresManualAutomático
InterceptoresNo
CancelaciónAbortControllerCancelToken
TimeoutManualConfigurable

Estados de carga
import { useState, useEffect } from 'react';
function Datos() {
// Estados para manejar la petición
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch('/api/data');
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
// Renderizado condicional
if (loading) {
return <LoadingSpinner />;
}
if (error) {
return <ErrorMessage message={error} />;
}
if (!data) {
return <EmptyState />;
}
return <DataDisplay data={data} />;
}
Componente Loading
function LoadingSpinner() {
return (
<div className="loading-container">
<div className="spinner"></div>
<p>Cargando...</p>
</div>
);
}
// CSS para el spinner
/*
.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
*/
Skeleton Loading
function SkeletonCard() {
return (
<div className="skeleton-card">
<div className="skeleton-image"></div>
<div className="skeleton-title"></div>
<div className="skeleton-text"></div>
<div className="skeleton-text short"></div>
</div>
);
}
function ListaProductos() {
const [productos, setProductos] = useState([]);
const [loading, setLoading] = useState(true);
// ... fetch
if (loading) {
return (
<div className="grid">
{[1, 2, 3, 4].map(i => <SkeletonCard key={i} />)}
</div>
);
}
return (
<div className="grid">
{productos.map(p => <ProductCard key={p.id} producto={p} />)}
</div>
);
}

TipoCódigoEjemplo
Cliente4xx404 Not Found, 401 Unauthorized
Servidor5xx500 Internal Server Error
Red-Sin conexión, timeout
Manejo de errores
import { useState, useEffect } from 'react';
function ListaConErrores() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch('/api/data');
// Verificar código de estado
if (response.status === 404) {
throw new Error('Recurso no encontrado');
}
if (response.status === 401) {
throw new Error('No autorizado. Por favor, inicia sesión');
}
if (!response.ok) {
throw new Error(`Error del servidor: ${response.status}`);
}
const result = await response.json();
setData(result);
} catch (err) {
// Diferenciar tipos de error
if (err.name === 'TypeError') {
setError('Error de conexión. Verifica tu internet.');
} else {
setError(err.message);
}
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
if (error) {
return (
<div className="error-container">
<p>{error}</p>
<button onClick={fetchData}>Reintentar</button>
</div>
);
}
// ... resto
}
Componente Error
function ErrorMessage({ message, onRetry }) {
return (
<div className="error-box">
<span className="error-icon">⚠️</span>
<p className="error-text">{message}</p>
{onRetry && (
<button onClick={onRetry} className="retry-btn">
Reintentar
</button>
)}
</div>
);
}
// Uso
<ErrorMessage
message="No se pudieron cargar los datos"
onRetry={fetchData}
/>

Hook usePosts
import { useState } from 'react';
import axios from 'axios';
const API_URL = 'https://jsonplaceholder.typicode.com/posts';
function usePosts() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// GET - Obtener todos
const fetchPosts = async () => {
setLoading(true);
try {
const { data } = await axios.get(API_URL);
setPosts(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
// POST - Crear
const createPost = async (newPost) => {
try {
const { data } = await axios.post(API_URL, newPost);
setPosts([...posts, data]);
return data;
} catch (err) {
setError(err.message);
throw err;
}
};
// PUT - Actualizar
const updatePost = async (id, updatedPost) => {
try {
const { data } = await axios.put(`${API_URL}/${id}`, updatedPost);
setPosts(posts.map(p => p.id === id ? data : p));
return data;
} catch (err) {
setError(err.message);
throw err;
}
};
// DELETE - Eliminar
const deletePost = async (id) => {
try {
await axios.delete(`${API_URL}/${id}`);
setPosts(posts.filter(p => p.id !== id));
} catch (err) {
setError(err.message);
throw err;
}
};
return {
posts,
loading,
error,
fetchPosts,
createPost,
updatePost,
deletePost
};
}
Componente CRUD
function GestorPosts() {
const {
posts,
loading,
error,
fetchPosts,
createPost,
deletePost
} = usePosts();
const [titulo, setTitulo] = useState('');
useEffect(() => {
fetchPosts();
}, []);
const handleCreate = async (e) => {
e.preventDefault();
await createPost({ title: titulo, body: 'Contenido...' });
setTitulo('');
};
const handleDelete = async (id) => {
if (confirm('¿Eliminar este post?')) {
await deletePost(id);
}
};
if (loading) return <p>Cargando...</p>;
if (error) return <p>Error: {error}</p>;
return (
<div>
<form onSubmit={handleCreate}>
<input
value={titulo}
onChange={e => setTitulo(e.target.value)}
placeholder="Título del post"
/>
<button type="submit">Crear Post</button>
</form>
<ul>
{posts.map(post => (
<li key={post.id}>
{post.title}
<button onClick={() => handleDelete(post.id)}>🗑️</button>
</li>
))}
</ul>
</div>
);
}

Configuración de Axios
// services/api.js
import axios from 'axios';
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
});
// Interceptor para agregar token
api.interceptors.request.use(
config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
error => Promise.reject(error)
);
// Interceptor para manejar errores
api.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
// Redirigir a login
window.location.href = '/login';
}
return Promise.reject(error);
}
);
export default api;
Uso de instancia
// En cualquier componente
import api from './services/api';
// GET
const { data } = await api.get('/users');
// POST
const { data } = await api.post('/users', { name: 'Juan' });
// PUT
const { data } = await api.put('/users/1', { name: 'Juan Actualizado' });
// DELETE
await api.delete('/users/1');

🐝