Blog

RA

Rod Alexanderson

Desarrollador Web

Creando documentación técnica en español para desarrolladores de Latinoamérica.

Más sobre mí →

Suscríbete al Newsletter

Recibe los nuevos artículos directamente en tu email.

Axios: Guía Completa para Peticiones HTTP

Publicado el 1 de octubre, 2025 • 20 min de lectura

Axios es la librería más popular para hacer peticiones HTTP en JavaScript. Si has usado fetch y te has encontrado escribiendo el mismo código repetitivo una y otra vez, Axios es la solución.

¿Qué es Axios?

Axios es una librería de JavaScript que hace que trabajar con peticiones HTTP sea más fácil y consistente. Es como fetch, pero con superpoderes.

Piensa en Axios como un asistente personal:

  • Fetch: Tienes que hacer todo manualmente (verificar errores, convertir JSON, configurar headers cada vez)
  • Axios: Tu asistente hace el trabajo pesado automáticamente (convierte JSON, maneja errores mejor, configuración reutilizable)

¿Por qué usar Axios en lugar de fetch?

Comparemos las dos opciones con un ejemplo real:

Con fetch (nativo)

async function obtenerUsuario(id) {
  try {
    const response = await fetch(`https://api.ejemplo.com/usuarios/${id}`)
    
    // Verificar manualmente si fue exitoso
    if (!response.ok) {
      throw new Error(`Error: ${response.status}`)
    }
    
    // Convertir manualmente a JSON
    const usuario = await response.json()
    return usuario
    
  } catch (error) {
    console.error('Error:', error)
    throw error
  }
}

Con Axios

async function obtenerUsuario(id) {
  try {
    const response = await axios.get(`https://api.ejemplo.com/usuarios/${id}`)
    
    // Los datos ya están en formato JSON
    return response.data
    
  } catch (error) {
    // Axios automáticamente lanza error si status no es 2xx
    console.error('Error:', error.message)
    throw error
  }
}

Ventajas de Axios:

  1. Convierte JSON automáticamente (no necesitas .json())
  2. Lanza errores automáticamente en status codes 4xx/5xx
  3. Sintaxis más corta y limpia
  4. Interceptors (interceptores) para transformar peticiones/respuestas
  5. Cancelación de peticiones más simple
  6. Timeouts (límite de tiempo) integrados
  7. Funciona igual en navegador y Node.js
  8. Transformación automática de datos
  9. Protección contra CSRF (Cross-Site Request Forgery)
  10. Soporte de progress (progreso) de subida/descarga
ℹ️
¿Cuándo usar cada uno?

Usa fetch si quieres evitar dependencias externas y tus necesidades son simples.

Usa Axios si necesitas funcionalidades avanzadas, mejor manejo de errores, o harás muchas peticiones HTTP en tu proyecto.

Instalación

Axios es una librería externa, así que necesitas instalarla:

Con npm

npm install axios

Con yarn

yarn add axios

Con pnpm

pnpm add axios

Desde CDN (para HTML)

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

Importar en tu código

// ES6 modules
import axios from 'axios'

// CommonJS (Node.js antiguo)
const axios = require('axios')

Sintaxis básica

GET: Obtener datos

La forma más simple de hacer una petición:

// Promesas con .then()
axios.get('https://jsonplaceholder.typicode.com/users')
  .then(response => {
    console.log(response.data)
  })
  .catch(error => {
    console.error('Error:', error)
  })

// Async/await (recomendado)
async function obtenerUsuarios() {
  try {
    const response = await axios.get('https://jsonplaceholder.typicode.com/users')
    console.log(response.data)
  } catch (error) {
    console.error('Error:', error)
  }
}

El objeto response:

const response = await axios.get('url')

console.log(response.data)       // Los datos de la respuesta
console.log(response.status)     // 200, 404, 500, etc.
console.log(response.statusText) // "OK", "Not Found", etc.
console.log(response.headers)    // Headers de la respuesta
console.log(response.config)     // Configuración de la petición

POST: Enviar datos

async function crearUsuario() {
  try {
    const response = await axios.post('https://jsonplaceholder.typicode.com/users', {
      nombre: 'Ana García',
      email: 'ana@ejemplo.com',
      edad: 25
    })
    
    console.log('Usuario creado:', response.data)
  } catch (error) {
    console.error('Error:', error)
  }
}

No necesitas JSON.stringify() ni configurar Content-Type manualmente. Axios lo hace automáticamente.

PUT: Actualizar datos completos

async function actualizarUsuario(id) {
  try {
    const response = await axios.put(`https://api.ejemplo.com/usuarios/${id}`, {
      nombre: 'Ana García Actualizada',
      email: 'ana.nueva@ejemplo.com',
      edad: 26
    })
    
    console.log('Usuario actualizado:', response.data)
  } catch (error) {
    console.error('Error:', error)
  }
}

PATCH: Actualizar datos parciales

async function actualizarEmail(id) {
  try {
    const response = await axios.patch(`https://api.ejemplo.com/usuarios/${id}`, {
      email: 'nuevo@ejemplo.com'  // Solo actualizar el email
    })
    
    console.log('Email actualizado:', response.data)
  } catch (error) {
    console.error('Error:', error)
  }
}

DELETE: Eliminar datos

async function eliminarUsuario(id) {
  try {
    const response = await axios.delete(`https://api.ejemplo.com/usuarios/${id}`)
    console.log('Usuario eliminado')
  } catch (error) {
    console.error('Error:', error)
  }
}

Configuración de peticiones

Axios permite configurar muchos aspectos de tus peticiones:

Sintaxis extendida

const response = await axios({
  method: 'post',                    // get, post, put, delete, etc.
  url: 'https://api.ejemplo.com/usuarios',
  data: {                            // Datos a enviar (body)
    nombre: 'Ana',
    email: 'ana@ejemplo.com'
  },
  headers: {                         // Headers personalizados
    'Authorization': 'Bearer token123'
  },
  params: {                          // Query parameters (?clave=valor)
    activo: true,
    rol: 'admin'
  },
  timeout: 5000,                     // Timeout en milisegundos
  responseType: 'json'               // json, text, blob, etc.
})

Query parameters

Los query parameters son los parámetros que van en la URL después del ?:

// Forma manual
const response = await axios.get('https://api.ejemplo.com/usuarios?edad=25&ciudad=Madrid')

// Forma con params (mejor)
const response = await axios.get('https://api.ejemplo.com/usuarios', {
  params: {
    edad: 25,
    ciudad: 'Madrid'
  }
})

// Ambas formas generan: /usuarios?edad=25&ciudad=Madrid

Headers personalizados

const response = await axios.get('https://api.ejemplo.com/datos', {
  headers: {
    'Authorization': 'Bearer mi-token-secreto',
    'Accept': 'application/json',
    'X-Custom-Header': 'valor-personalizado'
  }
})

Timeout (límite de tiempo)

// Si tarda más de 5 segundos, lanza error
const response = await axios.get('https://api.ejemplo.com/datos', {
  timeout: 5000  // 5 segundos
})

Instancias de Axios

En lugar de configurar cada petición, puedes crear una instancia con configuración predeterminada:

// Crear instancia con configuración base
const api = axios.create({
  baseURL: 'https://api.ejemplo.com',
  timeout: 5000,
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token123'
  }
})

// Usar la instancia
const usuarios = await api.get('/usuarios')           // https://api.ejemplo.com/usuarios
const usuario = await api.get('/usuarios/1')          // https://api.ejemplo.com/usuarios/1
const nuevo = await api.post('/usuarios', { datos })  // https://api.ejemplo.com/usuarios

Ventajas de las instancias:

  • Configuración reutilizable
  • URLs más cortas (solo necesitas la ruta, no la URL completa)
  • Diferentes instancias para diferentes APIs
  • Fácil de mantener

Ejemplo práctico: API de tienda online

// Crear instancia para tu API
const tiendaAPI = axios.create({
  baseURL: 'https://api.mitienda.com',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json'
  }
})

// Funciones reutilizables
async function obtenerProductos() {
  const response = await tiendaAPI.get('/productos')
  return response.data
}

async function obtenerProducto(id) {
  const response = await tiendaAPI.get(`/productos/${id}`)
  return response.data
}

async function crearProducto(datos) {
  const response = await tiendaAPI.post('/productos', datos)
  return response.data
}

async function actualizarProducto(id, datos) {
  const response = await tiendaAPI.put(`/productos/${id}`, datos)
  return response.data
}

async function eliminarProducto(id) {
  const response = await tiendaAPI.delete(`/productos/${id}`)
  return response.data
}

// Usar las funciones
const productos = await obtenerProductos()
const producto = await obtenerProducto(1)

Interceptors (Interceptores)

Los interceptors son funciones que se ejecutan antes de enviar una petición o después de recibir una respuesta. Son muy útiles para:

  • Añadir tokens de autenticación automáticamente
  • Transformar datos antes de enviar
  • Manejar errores globalmente
  • Mostrar/ocultar indicadores de carga
  • Logging de peticiones

Request Interceptor (antes de enviar)

// Crear instancia
const api = axios.create({
  baseURL: 'https://api.ejemplo.com'
})

// Interceptor de petición (request)
api.interceptors.request.use(
  (config) => {
    // Hacer algo antes de enviar la petición
    console.log('Enviando petición a:', config.url)
    
    // Por ejemplo, añadir token de autenticación
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    
    return config
  },
  (error) => {
    // Manejar error de petición
    return Promise.reject(error)
  }
)

// Ahora todas las peticiones incluirán el token automáticamente
const datos = await api.get('/datos-protegidos')

Response Interceptor (después de recibir)

// Interceptor de respuesta (response)
api.interceptors.response.use(
  (response) => {
    // Hacer algo con la respuesta exitosa
    console.log('Respuesta recibida:', response.status)
    return response
  },
  (error) => {
    // Manejar error de respuesta
    if (error.response) {
      // El servidor respondió con un status code fuera del rango 2xx
      if (error.response.status === 401) {
        console.log('No autorizado - redirigir a login')
        // window.location.href = '/login'
      } else if (error.response.status === 500) {
        console.log('Error del servidor')
      }
    } else if (error.request) {
      // La petición se hizo pero no hubo respuesta
      console.log('Sin respuesta del servidor')
    } else {
      // Algo pasó al configurar la petición
      console.log('Error:', error.message)
    }
    
    return Promise.reject(error)
  }
)

Ejemplo completo: Autenticación automática

const api = axios.create({
  baseURL: 'https://api.ejemplo.com',
  timeout: 10000
})

// Añadir token a todas las peticiones
api.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('auth_token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  },
  (error) => Promise.reject(error)
)

// Manejar errores de autenticación globalmente
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      // Token expirado o inválido
      localStorage.removeItem('auth_token')
      window.location.href = '/login'
    }
    return Promise.reject(error)
  }
)

// Todas las peticiones ahora:
// 1. Incluyen el token automáticamente
// 2. Redirigen a login si el token es inválido
const datosProtegidos = await api.get('/perfil')

Ejemplo: Loading global

let peticionesActivas = 0

api.interceptors.request.use(
  (config) => {
    peticionesActivas++
    // Mostrar spinner de carga
    document.getElementById('loading').style.display = 'block'
    return config
  },
  (error) => Promise.reject(error)
)

api.interceptors.response.use(
  (response) => {
    peticionesActivas--
    if (peticionesActivas === 0) {
      // Ocultar spinner cuando no hay peticiones activas
      document.getElementById('loading').style.display = 'none'
    }
    return response
  },
  (error) => {
    peticionesActivas--
    if (peticionesActivas === 0) {
      document.getElementById('loading').style.display = 'none'
    }
    return Promise.reject(error)
  }
)

Manejo de errores

Axios tiene un manejo de errores más robusto que fetch:

Estructura del error

try {
  const response = await axios.get('https://api.ejemplo.com/datos')
} catch (error) {
  if (error.response) {
    // El servidor respondió con un status fuera del rango 2xx
    console.log('Datos:', error.response.data)
    console.log('Status:', error.response.status)
    console.log('Headers:', error.response.headers)
  } else if (error.request) {
    // La petición se hizo pero no hubo respuesta
    console.log('No hubo respuesta:', error.request)
  } else {
    // Algo pasó al configurar la petición
    console.log('Error:', error.message)
  }
  console.log('Config:', error.config)
}

Manejo de diferentes status codes

async function obtenerDatos() {
  try {
    const response = await axios.get('https://api.ejemplo.com/datos')
    return response.data
  } catch (error) {
    if (error.response) {
      switch (error.response.status) {
        case 400:
          throw new Error('Petición inválida')
        case 401:
          throw new Error('No autorizado - inicia sesión')
        case 403:
          throw new Error('Acceso prohibido')
        case 404:
          throw new Error('Recurso no encontrado')
        case 500:
          throw new Error('Error del servidor')
        default:
          throw new Error(`Error: ${error.response.status}`)
      }
    } else if (error.request) {
      throw new Error('No se pudo conectar al servidor')
    } else {
      throw new Error('Error al configurar la petición')
    }
  }
}

Configurar validación de status

Por defecto, Axios rechaza promesas en status codes fuera del rango 200-299. Puedes cambiar esto:

const response = await axios.get('https://api.ejemplo.com/datos', {
  validateStatus: (status) => {
    // Considerar exitoso si status es menor a 500
    return status < 500
  }
})

// Ahora 404, 401, etc. no lanzan error
if (response.status === 404) {
  console.log('Recurso no encontrado')
}

Cancelar peticiones

Axios hace muy fácil cancelar peticiones:

Con AbortController (recomendado)

async function buscarDatos() {
  const controller = new AbortController()
  
  try {
    const response = await axios.get('https://api.ejemplo.com/buscar', {
      signal: controller.signal
    })
    
    console.log(response.data)
  } catch (error) {
    if (axios.isCancel(error)) {
      console.log('Petición cancelada:', error.message)
    } else {
      console.error('Error:', error)
    }
  }
  
  // Cancelar después de 5 segundos
  setTimeout(() => controller.abort(), 5000)
}

Ejemplo práctico: Búsqueda con cancelación

let controladorActual = null

async function buscar(query) {
  // Cancelar búsqueda anterior
  if (controladorActual) {
    controladorActual.abort()
  }
  
  // Crear nuevo controlador
  controladorActual = new AbortController()
  
  try {
    const response = await axios.get('https://api.ejemplo.com/buscar', {
      params: { q: query },
      signal: controladorActual.signal
    })
    
    mostrarResultados(response.data)
  } catch (error) {
    if (!axios.isCancel(error)) {
      console.error('Error:', error)
    }
  }
}

// En un input de búsqueda
input.addEventListener('input', (e) => {
  buscar(e.target.value)
})

Peticiones paralelas

Hacer múltiples peticiones al mismo tiempo:

Con Promise.all

async function obtenerTodosDatos() {
  try {
    const [usuarios, posts, comentarios] = await Promise.all([
      axios.get('https://jsonplaceholder.typicode.com/users'),
      axios.get('https://jsonplaceholder.typicode.com/posts'),
      axios.get('https://jsonplaceholder.typicode.com/comments')
    ])
    
    console.log('Usuarios:', usuarios.data.length)
    console.log('Posts:', posts.data.length)
    console.log('Comentarios:', comentarios.data.length)
  } catch (error) {
    console.error('Error:', error)
  }
}

Con axios.all (deprecated pero aún funciona)

axios.all([
  axios.get('url1'),
  axios.get('url2'),
  axios.get('url3')
])
.then(axios.spread((response1, response2, response3) => {
  console.log(response1.data)
  console.log(response2.data)
  console.log(response3.data)
}))

Subir archivos

Axios hace fácil subir archivos:

Subir un archivo

async function subirArchivo(archivo) {
  const formData = new FormData()
  formData.append('archivo', archivo)
  formData.append('descripcion', 'Mi archivo')
  
  try {
    const response = await axios.post('https://api.ejemplo.com/upload', formData, {
      headers: {
        'Content-Type': 'multipart/form-data'
      },
      onUploadProgress: (progressEvent) => {
        const porcentaje = (progressEvent.loaded / progressEvent.total) * 100
        console.log(`Subiendo: ${porcentaje.toFixed(2)}%`)
      }
    })
    
    console.log('Archivo subido:', response.data.url)
  } catch (error) {
    console.error('Error al subir:', error)
  }
}

// Usar con input file
const input = document.querySelector('input[type="file"]')
input.addEventListener('change', (e) => {
  const archivo = e.target.files[0]
  subirArchivo(archivo)
})

Subir múltiples archivos

async function subirVariosArchivos(archivos) {
  const formData = new FormData()
  
  // Añadir múltiples archivos
  for (let i = 0; i < archivos.length; i++) {
    formData.append('archivos', archivos[i])
  }
  
  try {
    const response = await axios.post('https://api.ejemplo.com/upload-multiple', formData, {
      headers: {
        'Content-Type': 'multipart/form-data'
      },
      onUploadProgress: (progressEvent) => {
        const porcentaje = (progressEvent.loaded / progressEvent.total) * 100
        actualizarBarraProgreso(porcentaje)
      }
    })
    
    console.log('Archivos subidos:', response.data)
  } catch (error) {
    console.error('Error:', error)
  }
}

Ejemplos prácticos completos

Ejemplo 1: Sistema de autenticación

const api = axios.create({
  baseURL: 'https://api.ejemplo.com'
})

// Añadir token a todas las peticiones
api.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  }
)

// Login
async function login(email, password) {
  try {
    const response = await api.post('/auth/login', {
      email,
      password
    })
    
    // Guardar token
    localStorage.setItem('token', response.data.token)
    
    return response.data.usuario
  } catch (error) {
    if (error.response?.status === 401) {
      throw new Error('Credenciales inválidas')
    }
    throw new Error('Error al iniciar sesión')
  }
}

// Registro
async function registrar(datos) {
  try {
    const response = await api.post('/auth/registrar', datos)
    
    // Guardar token
    localStorage.setItem('token', response.data.token)
    
    return response.data.usuario
  } catch (error) {
    if (error.response?.status === 400) {
      throw new Error('Datos inválidos')
    }
    throw new Error('Error al registrar')
  }
}

// Cerrar sesión
function logout() {
  localStorage.removeItem('token')
  window.location.href = '/login'
}

// Obtener perfil (requiere autenticación)
async function obtenerPerfil() {
  try {
    const response = await api.get('/perfil')
    return response.data
  } catch (error) {
    if (error.response?.status === 401) {
      logout()
    }
    throw error
  }
}

Ejemplo 2: CRUD de productos

const api = axios.create({
  baseURL: 'https://api.ejemplo.com/productos'
})

// CREATE - Crear producto
async function crearProducto(datos) {
  try {
    const response = await api.post('/', datos)
    return response.data
  } catch (error) {
    throw new Error('Error al crear producto')
  }
}

// READ - Obtener todos los productos
async function obtenerProductos(filtros = {}) {
  try {
    const response = await api.get('/', {
      params: filtros  // ?categoria=ropa&precio_max=100
    })
    return response.data
  } catch (error) {
    throw new Error('Error al obtener productos')
  }
}

// READ - Obtener un producto
async function obtenerProducto(id) {
  try {
    const response = await api.get(`/${id}`)
    return response.data
  } catch (error) {
    if (error.response?.status === 404) {
      throw new Error('Producto no encontrado')
    }
    throw new Error('Error al obtener producto')
  }
}

// UPDATE - Actualizar producto
async function actualizarProducto(id, datos) {
  try {
    const response = await api.put(`/${id}`, datos)
    return response.data
  } catch (error) {
    throw new Error('Error al actualizar producto')
  }
}

// DELETE - Eliminar producto
async function eliminarProducto(id) {
  try {
    await api.delete(`/${id}`)
    return true
  } catch (error) {
    throw new Error('Error al eliminar producto')
  }
}

// Usar las funciones
async function ejemplo() {
  // Crear
  const nuevo = await crearProducto({
    nombre: 'Camisa',
    precio: 25,
    stock: 100
  })
  
  // Leer todos con filtros
  const productos = await obtenerProductos({
    categoria: 'ropa',
    precio_max: 50
  })
  
  // Leer uno
  const producto = await obtenerProducto(1)
  
  // Actualizar
  const actualizado = await actualizarProducto(1, {
    precio: 30
  })
  
  // Eliminar
  await eliminarProducto(1)
}

Ejemplo 3: Buscador con debounce

let timeoutId = null
let controladorBusqueda = null

async function buscar(query) {
  // Limpiar timeout anterior
  if (timeoutId) {
    clearTimeout(timeoutId)
  }
  
  // Cancelar búsqueda anterior
  if (controladorBusqueda) {
    controladorBusqueda.abort()
  }
  
  // Esperar 500ms antes de buscar (debounce)
  timeoutId = setTimeout(async () => {
    if (query.length < 3) {
      mostrarResultados([])
      return
    }
    
    controladorBusqueda = new AbortController()
    
    try {
      const response = await axios.get('https://api.ejemplo.com/buscar', {
        params: { q: query },
        signal: controladorBusqueda.signal
      })
      
      mostrarResultados(response.data)
    } catch (error) {
      if (!axios.isCancel(error)) {
        console.error('Error en búsqueda:', error)
        mostrarResultados([])
      }
    }
  }, 500)
}

// En el input
const input = document.getElementById('busqueda')
input.addEventListener('input', (e) => {
  buscar(e.target.value)
})

function mostrarResultados(resultados) {
  const contenedor = document.getElementById('resultados')
  contenedor.innerHTML = ''
  
  resultados.forEach(resultado => {
    const div = document.createElement('div')
    div.textContent = resultado.nombre
    contenedor.appendChild(div)
  })
}

Ejemplo 4: Paginación

async function cargarProductos(pagina = 1) {
  const contenedor = document.getElementById('productos')
  contenedor.innerHTML = '<p>Cargando...</p>'
  
  try {
    const response = await axios.get('https://api.ejemplo.com/productos', {
      params: {
        pagina: pagina,
        limite: 20
      }
    })
    
    const { datos, paginaActual, totalPaginas } = response.data
    
    // Mostrar productos
    contenedor.innerHTML = ''
    datos.forEach(producto => {
      const div = document.createElement('div')
      div.innerHTML = `
        <h3>${producto.nombre}</h3>
        <p>$${producto.precio}</p>
      `
      contenedor.appendChild(div)
    })
    
    // Mostrar botones de paginación
    crearBotonesPaginacion(paginaActual, totalPaginas)
    
  } catch (error) {
    contenedor.innerHTML = '<p>Error al cargar productos</p>'
  }
}

function crearBotonesPaginacion(actual, total) {
  const contenedor = document.getElementById('paginacion')
  contenedor.innerHTML = ''
  
  // Botón anterior
  if (actual > 1) {
    const btnAnterior = document.createElement('button')
    btnAnterior.textContent = 'Anterior'
    btnAnterior.onclick = () => cargarProductos(actual - 1)
    contenedor.appendChild(btnAnterior)
  }
  
  // Número de página
  const span = document.createElement('span')
  span.textContent = `Página ${actual} de ${total}`
  contenedor.appendChild(span)
  
  // Botón siguiente
  if (actual < total) {
    const btnSiguiente = document.createElement('button')
    btnSiguiente.textContent = 'Siguiente'
    btnSiguiente.onclick = () => cargarProductos(actual + 1)
    contenedor.appendChild(btnSiguiente)
  }
}

// Cargar primera página
cargarProductos(1)

Axios en React

Hook personalizado

import { useState, useEffect } from 'react'
import axios from 'axios'

function useFetch(url) {
  const [datos, setDatos] = useState(null)
  const [cargando, setCargando] = useState(true)
  const [error, setError] = useState(null)
  
  useEffect(() => {
    const controller = new AbortController()
    
    async function cargar() {
      try {
        setCargando(true)
        const response = await axios.get(url, {
          signal: controller.signal
        })
        setDatos(response.data)
        setError(null)
      } catch (err) {
        if (!axios.isCancel(err)) {
          setError(err.message)
        }
      } finally {
        setCargando(false)
      }
    }
    
    cargar()
    
    return () => controller.abort()
  }, [url])
  
  return { datos, cargando, error }
}

// Usar el hook
function ProductosComponent() {
  const { datos, cargando, error } = useFetch('https://api.ejemplo.com/productos')
  
  if (cargando) return <div>Cargando...</div>
  if (error) return <div>Error: {error}</div>
  
  return (
    <div>
      {datos.map(producto => (
        <div key={producto.id}>{producto.nombre}</div>
      ))}
    </div>
  )
}

Diferencias: Fetch vs Axios

Comparación lado a lado:

CaracterísticaFetchAxios
Nativo del navegador✓ Sí✗ No (dependencia)
Conversión JSON automática✗ No (.json())✓ Sí
Manejo de errores HTTP✗ Manual✓ Automático
Interceptors✗ No✓ Sí
Timeout✗ Manual con AbortSignal✓ Integrado
Progress✗ Complejo✓ Simple
Transformación de datos✗ Manual✓ Automática
Node.js compatible✓ Desde v18✓ Siempre
Tamaño0 KB (nativo)~15 KB

Errores comunes

Error 1: No usar try/catch

// ❌ Incorrecto
async function obtenerDatos() {
  const response = await axios.get('url')
  console.log(response.data)
}

// ✓ Correcto
async function obtenerDatos() {
  try {
    const response = await axios.get('url')
    console.log(response.data)
  } catch (error) {
    console.error('Error:', error)
  }
}

Error 2: Olvidar .data

// ❌ Incorrecto
const response = await axios.get('url')
return response  // Retorna todo el objeto response

// ✓ Correcto
const response = await axios.get('url')
return response.data  // Retorna solo los datos

Error 3: Configurar headers innecesarios en FormData

// ❌ Incorrecto
const formData = new FormData()
formData.append('archivo', archivo)

await axios.post('url', formData, {
  headers: {
    'Content-Type': 'multipart/form-data'  // No necesario, se añade automáticamente
  }
})

// ✓ Correcto
await axios.post('url', formData)  // Axios configura el Content-Type automáticamente

Error 4: No cancelar peticiones

// ❌ Incorrecto: peticiones múltiples sin cancelar
function buscar(query) {
  axios.get(`/buscar?q=${query}`)
    .then(response => mostrarResultados(response.data))
}

input.addEventListener('input', (e) => {
  buscar(e.target.value)  // Múltiples peticiones simultáneas
})

// ✓ Correcto: cancelar petición anterior
let controller = null

async function buscar(query) {
  if (controller) controller.abort()
  controller = new AbortController()
  
  const response = await axios.get(`/buscar?q=${query}`, {
    signal: controller.signal
  })
  mostrarResultados(response.data)
}

Resumen

Puntos clave sobre Axios:

  1. Librería externa que simplifica peticiones HTTP
  2. Convierte JSON automáticamente
  3. Lanza errores en status codes 4xx/5xx
  4. Interceptors para transformar peticiones/respuestas
  5. Instancias con configuración reutilizable
  6. Cancelación de peticiones más simple
  7. Timeout integrado
  8. Progreso de subida/descarga
  9. Funciona igual en navegador y Node.js
  10. Mejor manejo de errores que fetch

Cuándo usar Axios:

  • Necesitas funcionalidades avanzadas (interceptors, timeouts)
  • Harás muchas peticiones HTTP en tu proyecto
  • Quieres código más limpio y menos repetitivo
  • Necesitas transformaciones automáticas
  • Quieres mejor manejo de errores

Cuándo usar fetch:

  • Proyecto simple con pocas peticiones
  • Quieres evitar dependencias externas
  • El tamaño del bundle es crítico
  • Solo necesitas peticiones básicas GET/POST

Recursos adicionales:


Sobre el autor: Desarrollador especializado en JavaScript, React y NextJS. Creo contenido educativo en español para ayudar a la comunidad de desarrolladores.