Node
Tutorial Node: Validación de datos
Node.js middlewares personalizados para validación de datos. Aprende a asegurar entrada válida y manejo de errores eficiente.
Aprende Node GRATIS y certifícateCreación de middlewares de validación
La validación de datos es esencial en cualquier aplicación que interactúa con usuarios o sistemas externos. En Node.js, crear middlewares personalizados permite interceptar y procesar las peticiones antes de que lleguen a las rutas finales. A continuación, se muestra cómo crear un middleware de validación manualmente.
Para empezar, definimos una función que actúa como middleware. Esta función recibe los parámetros req
, res
y next
, y utiliza las propiedades de la solicitud para realizar la validación:
function validarEntrada(req, res, next) {
const { nombre, edad } = req.body;
if (!nombre || typeof nombre !== "string") {
return res
.status(400)
.send({ error: 'El campo "nombre" es obligatorio y debe ser un string.' });
}
if (!edad || typeof edad !== "number") {
return res.status(400).send({ error: 'El campo "edad" es obligatorio y debe ser un número.' });
}
next();
}
Este middleware verifica que nombre
y edad
estén presentes y sean del tipo correcto. Si alguna validación falla, se envía una respuesta con código de estado 400 y un mensaje de error descriptivo.
Para utilizar este middleware, se aplica a las rutas correspondientes:
import { createServer } from "http";
const server = createServer((req, res) => {
// Suponiendo que el método y la ruta son POST /usuarios
if (req.method === "POST" && req.url === "/usuarios") {
// Parsear el cuerpo de la solicitud
let cuerpo = "";
req.on("data", (chunk) => {
cuerpo += chunk.toString();
});
req.on("end", () => {
req.body = JSON.parse(cuerpo);
// Llamar al middleware de validación
validarEntrada(req, res, () => {
// Procesar la solicitud si la validación es exitosa
res.statusCode = 201;
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({ mensaje: "Usuario creado exitosamente." }));
});
});
} else {
res.statusCode = 404;
res.end();
}
});
server.listen(3000, () => {
console.log("Servidor ejecutando en el puerto http://localhost:3000");
});
En este ejemplo, el servidor parsea manualmente el cuerpo de la solicitud y luego aplica el middleware validarEntrada
. Si la validación pasa, se envía una respuesta de éxito.
Es importante destacar que los middlewares pueden ser reutilizados en múltiples rutas, lo que mejora la modularidad y el mantenimiento del código.
Para manejar diferentes tipos de validaciones, se pueden crear middlewares adicionales o parametrizar los existentes. Por ejemplo, un middleware genérico para validar la presencia de campos:
function camposRequeridos(...campos) {
return (req, res, next) => {
for (const campo of campos) {
if (!req.body[campo]) {
return res.status(400).send({ error: `El campo "${campo}" es obligatorio.` });
}
}
next();
};
}
Este middleware se puede aplicar de la siguiente manera:
// Aplicar middleware para verificar que "email" y "password" estén presentes
app.post('/login', camposRequeridos('email', 'password'), (req, res) => {
// Procesar login
});
La creación manual de middlewares de validación proporciona flexibilidad y control total sobre cómo se manejan las entradas en la aplicación. Sin embargo, requiere atención para evitar errores y duplicación de código. Por ello, es recomendable estructurar el código de manera que los middlewares sean fácilmente accesibles y mantenibles.
Manejo de errores y respuestas coherentes (códigos de estado, mensajes)
Manejar los errores de forma adecuada es crucial para ofrecer una API REST fiable y fácil de consumir. Utilizar códigos de estado HTTP correctos y proporcionar mensajes claros ayuda a los clientes a entender y manejar las respuestas de la API.
Cuando se produce un error en la validación de datos, es recomendable responder con un código 400 Bad Request. Este código indica que el servidor no puede o no procesará la petición debido a un error del cliente:
if (!req.body.email) {
res.statusCode = 400;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: 'El campo "email" es obligatorio.' }));
return;
}
Si el error se debe a que el cliente no está autorizado para acceder a un recurso, se debe utilizar el código 401 Unauthorized. Este código informa al cliente que necesita autenticarse:
if (!usuarioAutenticado) {
res.statusCode = 401;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: 'Autenticación requerida.' }));
return;
}
En el caso de peticiones a recursos inexistentes, el código 404 Not Found es el apropiado. Indica que el servidor no pudo encontrar el recurso solicitado:
if (!recursoEncontrado) {
res.statusCode = 404;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: 'Recurso no encontrado.' }));
return;
}
Es importante manejar los errores del servidor correctamente. Si ocurre un error inesperado, se debe responder con un código 500 Internal Server Error sin exponer detalles sensibles:
try {
// Operaciones que pueden generar errores
} catch (error) {
console.error('Error interno del servidor:', error);
res.statusCode = 500;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: 'Error interno del servidor.' }));
}
Proporcionar mensajes de error consistentes mejora la usabilidad de la API. Se puede definir un formato estándar para las respuestas de error, incluyendo campos como codigo
, mensaje
y detalles
:
function enviarError(res, codigo, mensaje, detalles) {
res.statusCode = codigo;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({
error: {
codigo,
mensaje,
detalles
}
}));
}
Este enfoque permite enviar respuestas de error coherentes:
enviarError(res, 400, 'Datos inválidos', 'El campo "nombre" no puede estar vacío.');
Algunas buenas prácticas en el manejo de errores incluyen:
- No revelar información sensible en los mensajes de error.
- Registrar los errores en el servidor para su posterior análisis.
- Mantener coherencia en los códigos de estado y formatos de respuesta.
Centralizar el manejo de errores mediante middlewares permite un control más organizado. Un middleware de captura de errores puede interceptar excepciones y enviar una respuesta adecuada:
function manejadorDeErrores(err, req, res, next) {
console.error('Error:', err);
const codigo = err.codigo || 500;
const mensaje = codigo === 500 ? 'Error interno del servidor.' : err.mensaje;
res.statusCode = codigo;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: mensaje }));
}
Al lanzar errores en el código, se pueden incluir propiedades personalizadas para mejorar la información:
function validarUsuario(usuario) {
if (!usuario.nombre) {
const error = new Error('El campo "nombre" es obligatorio.');
error.codigo = 400;
throw error;
}
// Más validaciones...
}
En la ruta, se captura el error y se delega al middleware:
try {
validarUsuario(req.body);
res.statusCode = 200;
res.end(JSON.stringify({ mensaje: 'Usuario válido.' }));
} catch (err) {
manejadorDeErrores(err, req, res);
}
Es esencial definir y utilizar códigos de estado HTTP estándar para comunicar el resultado de las operaciones:
- 200 OK: Operación exitosa.
- 201 Created: Recurso creado satisfactoriamente.
- 204 No Content: Operación exitosa sin contenido en la respuesta.
- 400 Bad Request: Petición inválida por errores del cliente.
- 401 Unauthorized: Autenticación requerida.
- 403 Forbidden: El cliente no tiene permisos para acceder al recurso.
- 404 Not Found: Recurso no encontrado.
- 500 Internal Server Error: Error en el servidor.
Implementar códigos de estado y mensajes coherentes facilita la interacción con la API y reduce la complejidad en el manejo de errores por parte del cliente. Además, una documentación clara de los posibles errores y respuestas esperadas es fundamental para los desarrolladores que consumen la API.
Buenas prácticas para la validación de datos en Node.js
La validación de datos es un pilar fundamental para garantizar la seguridad y el correcto funcionamiento de las aplicaciones en Node.js. Aplicar buenas prácticas en este ámbito minimiza riesgos y mejora la calidad del software.
Una de las principales recomendaciones es validar siempre en el servidor, independientemente de las validaciones realizadas en el cliente. Confiar únicamente en el lado del cliente es un error, ya que las peticiones pueden ser manipuladas o enviadas directamente al servidor sin pasar por las verificaciones del frontend.
Es esencial centralizar las reglas de validación. Definir los criterios en un solo lugar evita duplicación de código y facilita el mantenimiento. Esto se logra creando módulos o funciones dedicadas exclusivamente a la validación, promoviendo la reutilización y consistencia en toda la aplicación.
Implementar validaciones exhaustivas que verifiquen no solo la presencia de campos, sino también su formato, tipo de dato y restricciones específicas. Por ejemplo, asegurar que un correo electrónico tenga un formato correcto o que una contraseña cumpla con requisitos de complejidad.
La sanitización de entradas es igualmente importante. Antes de procesar los datos, es recomendable limpiar las entradas para eliminar código o caracteres potencialmente maliciosos. Esto ayuda a prevenir vulnerabilidades como inyecciones de código o ataques de cross-site scripting (XSS).
Utilizar mensajes de error claros y consistentes mejora la experiencia del usuario y facilita la depuración. Los mensajes deben indicar explícitamente qué dato es inválido y por qué, sin revelar información sensible o detalles internos del sistema.
Adoptar un estándar en los códigos de estado HTTP utilizados al responder a errores de validación. Generalmente, se emplea el código 400 Bad Request para indicar al cliente que la solicitud no pudo ser procesada debido a datos inválidos.
Se recomienda el uso de tipado estricto y verificación de tipos en las validaciones. Aunque JavaScript es un lenguaje dinámico, es posible verificar los tipos utilizando el operador typeof
o instanceof
según corresponda.
if (typeof req.body.edad !== 'number') {
res.statusCode = 400;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: 'El campo "edad" debe ser un número.' }));
return;
}
Es importante no bloquear el event loop durante las validaciones. Las operaciones deben ser eficientes para no afectar el rendimiento del servidor. Evitar funciones síncronas que puedan bloquear la ejecución, especialmente en entornos con alta concurrencia.
El manejo de validaciones asincrónicas debe realizarse correctamente. Si las validaciones requieren operaciones como consultas a bases de datos, es fundamental utilizar async/await o promesas para asegurar que el flujo de la aplicación se mantiene coherente.
async function validarUsuarioUnico(req, res, next) {
try {
const usuarioExiste = await buscarUsuarioPorEmail(req.body.email);
if (usuarioExiste) {
res.statusCode = 400;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: 'El email ya está registrado.' }));
return;
}
next();
} catch (error) {
next(error);
}
}
Mantener las dependencias al mínimo necesario es una buena práctica. Si se opta por utilizar librerías externas para la validación, es recomendable seleccionar aquellas que estén activamente mantenidas y sean ampliamente utilizadas en la comunidad.
Finalmente, es crucial realizar pruebas exhaustivas de las validaciones implementadas. El testing ayuda a garantizar que las reglas funcionan como se espera y que el sistema responde adecuadamente ante diferentes escenarios y tipos de datos.
Todas las lecciones de Node
Accede a todas las lecciones de Node y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Node.js
Introducción Y Entorno
Fundamentos Del Entorno Node.js
Introducción Y Entorno
Módulo Http Y Https
Http Y Api Rest
Http Params, Headers Y Body
Http Y Api Rest
Validación De Datos
Http Y Api Rest
Conexión A Bases De Datos Sin Orm
Persistencia
Creación De Consultas Básicas (Crud) Sin Orm
Persistencia
Módulo Fs
Sistema De Archivos
Introducción A La Seguridad
Seguridad
Sesiones Y Cookies
Seguridad
Roles Y Permisos
Seguridad
Testing En Node.js
Testing
Estructura De Carpetas
Arquitectura
Configuración Y Variables De Entorno
Arquitectura
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la importancia de la validación de datos en aplicaciones de Node.js.
- Aprender a crear middlewares de validación personalizados.
- Aplicar buenas prácticas en el manejo de errores y codificación de respuestas HTTP.
- Implementar pruebas exhaustivas de las validaciones diseñadas.