Express

Express

Tutorial Express: Estados HTTP

Aprende a usar códigos de estado HTTP en Express para gestionar respuestas y errores en tus APIs REST de forma eficiente y profesional.

Aprende Express y certifícate

Códigos de estado HTTP

Los códigos de estado HTTP son números de tres dígitos que el servidor web envía al cliente para indicar el resultado de una petición. Estos códigos forman parte fundamental del protocolo HTTP y permiten que tanto el navegador como las aplicaciones cliente comprendan qué ha ocurrido con su solicitud.

En Express, cada respuesta que enviamos incluye automáticamente un código de estado. Si no especificamos ninguno, Express asigna por defecto el código 200 (OK) para respuestas exitosas. Sin embargo, es una buena práctica establecer códigos de estado apropiados según el contexto de cada respuesta.

Categorías de códigos de estado

Los códigos de estado se organizan en cinco categorías principales, cada una identificada por el primer dígito:

  • 1xx - Respuestas informativas: Indican que la petición se ha recibido y el proceso continúa
  • 2xx - Respuestas exitosas: La petición se ha recibido, entendido y procesado correctamente
  • 3xx - Redirecciones: Se necesita realizar alguna acción adicional para completar la petición
  • 4xx - Errores del cliente: La petición contiene sintaxis incorrecta o no puede procesarse
  • 5xx - Errores del servidor: El servidor falló al intentar procesar una petición válida

Códigos más utilizados en aplicaciones Express

En el desarrollo con Express, algunos códigos de estado aparecen con mayor frecuencia:

Códigos 2xx (Éxito):

app.get('/usuarios', (req, res) => {
  const usuarios = obtenerUsuarios();
  res.status(200).json(usuarios); // OK - Petición exitosa
});

app.post('/usuarios', (req, res) => {
  const nuevoUsuario = crearUsuario(req.body);
  res.status(201).json(nuevoUsuario); // Created - Recurso creado
});

app.delete('/usuarios/:id', (req, res) => {
  eliminarUsuario(req.params.id);
  res.status(204).send(); // No Content - Eliminación exitosa
});

Códigos 4xx (Errores del cliente):

app.get('/usuarios/:id', (req, res) => {
  const usuario = buscarUsuario(req.params.id);
  
  if (!usuario) {
    return res.status(404).json({ 
      error: 'Usuario no encontrado' 
    }); // Not Found
  }
  
  res.json(usuario);
});

app.post('/login', (req, res) => {
  const { email, password } = req.body;
  
  if (!email || !password) {
    return res.status(400).json({ 
      error: 'Email y contraseña son obligatorios' 
    }); // Bad Request
  }
  
  // Lógica de autenticación...
});

Códigos 5xx (Errores del servidor):

app.get('/datos', (req, res) => {
  try {
    const datos = procesarDatos();
    res.json(datos);
  } catch (error) {
    console.error('Error procesando datos:', error);
    res.status(500).json({ 
      error: 'Error interno del servidor' 
    }); // Internal Server Error
  }
});

Códigos específicos para APIs REST

Cuando desarrollamos APIs REST con Express, ciertos códigos de estado tienen significados específicos según la operación HTTP:

Para operaciones GET:

app.get('/productos/:id', (req, res) => {
  const producto = buscarProducto(req.params.id);
  
  if (!producto) {
    return res.status(404).json({ error: 'Producto no encontrado' });
  }
  
  res.status(200).json(producto);
});

Para operaciones POST:

app.post('/productos', (req, res) => {
  const { nombre, precio } = req.body;
  
  if (!nombre || precio === undefined) {
    return res.status(400).json({ 
      error: 'Nombre y precio son obligatorios' 
    });
  }
  
  const nuevoProducto = crearProducto({ nombre, precio });
  res.status(201).json(nuevoProducto);
});

Para operaciones PUT y PATCH:

app.put('/productos/:id', (req, res) => {
  const producto = buscarProducto(req.params.id);
  
  if (!producto) {
    return res.status(404).json({ error: 'Producto no encontrado' });
  }
  
  const productoActualizado = actualizarProducto(req.params.id, req.body);
  res.status(200).json(productoActualizado);
});

Códigos de redirección

Los códigos 3xx son especialmente útiles cuando necesitamos redirigir al cliente a otra ubicación:

app.get('/perfil', (req, res) => {
  if (!req.session.usuario) {
    return res.status(302).redirect('/login'); // Found - Redirección temporal
  }
  
  res.render('perfil');
});

app.get('/producto-antiguo/:id', (req, res) => {
  // Redirección permanente a nueva URL
  res.status(301).redirect(`/productos/${req.params.id}`);
});

Buenas prácticas con códigos de estado

Es importante ser consistente en el uso de códigos de estado a lo largo de toda la aplicación. Esto facilita tanto el desarrollo como el consumo de la API:

// Estructura consistente para errores
app.get('/api/usuarios/:id', (req, res) => {
  try {
    const usuario = buscarUsuario(req.params.id);
    
    if (!usuario) {
      return res.status(404).json({
        success: false,
        error: 'Usuario no encontrado',
        code: 'USER_NOT_FOUND'
      });
    }
    
    res.status(200).json({
      success: true,
      data: usuario
    });
    
  } catch (error) {
    res.status(500).json({
      success: false,
      error: 'Error interno del servidor',
      code: 'INTERNAL_ERROR'
    });
  }
});

La elección correcta del código de estado mejora significativamente la experiencia del desarrollador que consume nuestra API, ya que proporciona información clara sobre el resultado de cada operación sin necesidad de analizar el contenido de la respuesta.

Uso de res.status()

El método res.status() es la forma principal de establecer códigos de estado HTTP en Express. Este método pertenece al objeto de respuesta y nos permite definir explícitamente qué código queremos enviar al cliente antes de enviar la respuesta final.

La sintaxis básica de res.status() es sencilla: acepta un número entero que representa el código de estado HTTP y devuelve el objeto de respuesta, lo que permite encadenar otros métodos:

app.get('/ejemplo', (req, res) => {
  res.status(200).json({ mensaje: 'Éxito' });
});

Encadenamiento de métodos

Una de las características más útiles de res.status() es que devuelve el objeto de respuesta, permitiendo el encadenamiento fluido con otros métodos de respuesta:

app.post('/usuarios', (req, res) => {
  const nuevoUsuario = { id: 1, nombre: 'Juan' };
  
  // Encadenamiento directo
  res.status(201).json(nuevoUsuario);
});

app.delete('/usuarios/:id', (req, res) => {
  // También funciona con send()
  res.status(204).send();
});

app.get('/error', (req, res) => {
  // Y con cualquier otro método de respuesta
  res.status(500).render('error', { mensaje: 'Error interno' });
});

Establecimiento condicional del estado

El método res.status() es especialmente útil cuando necesitamos establecer diferentes códigos según las condiciones de nuestra lógica de negocio:

app.put('/productos/:id', (req, res) => {
  const { id } = req.params;
  const datosActualizacion = req.body;
  
  const producto = buscarProducto(id);
  
  if (!producto) {
    return res.status(404).json({ 
      error: 'Producto no encontrado' 
    });
  }
  
  if (!datosActualizacion.nombre) {
    return res.status(400).json({ 
      error: 'El nombre es obligatorio' 
    });
  }
  
  const productoActualizado = actualizarProducto(id, datosActualizacion);
  res.status(200).json(productoActualizado);
});

Diferencia con statusCode

Express también proporciona la propiedad res.statusCode para establecer códigos de estado, pero res.status() es la forma recomendada por varias razones:

app.get('/comparacion', (req, res) => {
  // Forma no recomendada
  res.statusCode = 201;
  res.json({ mensaje: 'Creado' });
  
  // Forma recomendada
  res.status(201).json({ mensaje: 'Creado' });
});

La ventaja principal de res.status() es que permite el encadenamiento y hace el código más legible y expresivo.

Validación automática

Express valida automáticamente que el código de estado proporcionado sea un número válido. Si pasamos un valor inválido, Express lanzará un error:

app.get('/invalido', (req, res) => {
  try {
    // Esto causará un error
    res.status('invalid').json({ data: 'test' });
  } catch (error) {
    console.error('Error:', error.message);
    res.status(500).json({ error: 'Error interno' });
  }
});

Uso en funciones auxiliares

Una práctica común es crear funciones auxiliares que encapsulen el uso de res.status() para respuestas frecuentes:

// Funciones auxiliares para respuestas comunes
function enviarExito(res, data) {
  return res.status(200).json({ success: true, data });
}

function enviarCreado(res, data) {
  return res.status(201).json({ success: true, data });
}

function enviarError(res, codigo, mensaje) {
  return res.status(codigo).json({ success: false, error: mensaje });
}

// Uso en las rutas
app.get('/usuarios/:id', (req, res) => {
  const usuario = buscarUsuario(req.params.id);
  
  if (!usuario) {
    return enviarError(res, 404, 'Usuario no encontrado');
  }
  
  enviarExito(res, usuario);
});

app.post('/usuarios', (req, res) => {
  const nuevoUsuario = crearUsuario(req.body);
  enviarCreado(res, nuevoUsuario);
});

Establecimiento múltiple

Es importante recordar que res.status() debe llamarse antes de enviar la respuesta. Una vez que se ha enviado la respuesta con métodos como json(), send() o render(), no es posible modificar el código de estado:

app.get('/incorrecto', (req, res) => {
  res.json({ mensaje: 'Datos enviados' });
  
  // Esto no tendrá efecto y puede causar errores
  // res.status(201); // ❌ Demasiado tarde
});

app.get('/correcto', (req, res) => {
  // Primero establecemos el estado
  res.status(201);
  
  // Luego enviamos la respuesta
  res.json({ mensaje: 'Datos enviados' });
});

Uso con async/await

Cuando trabajamos con operaciones asíncronas, res.status() se integra perfectamente con async/await:

app.post('/procesar', async (req, res) => {
  try {
    const resultado = await procesarDatosAsincrono(req.body);
    
    if (!resultado) {
      return res.status(422).json({ 
        error: 'No se pudieron procesar los datos' 
      });
    }
    
    res.status(200).json(resultado);
    
  } catch (error) {
    console.error('Error en procesamiento:', error);
    res.status(500).json({ 
      error: 'Error interno del servidor' 
    });
  }
});

El método res.status() es fundamental para crear APIs HTTP semánticamente correctas, ya que permite comunicar de forma precisa el resultado de cada operación al cliente que consume nuestra aplicación Express.

Aprende Express online

Otras lecciones de Express

Accede a todas las lecciones de Express y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Express y certifícate

Ejercicios de programación de Express

Evalúa tus conocimientos de esta lección Estados HTTP con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.