Express

Express

Tutorial Express: Registro de usuarios

Aprende a crear un sistema de registro de usuarios en Express 5 con validación, manejo de errores y almacenamiento simulado.

Aprende Express y certifícate

Implementar registro de usuarios

El registro de usuarios es una funcionalidad fundamental en cualquier aplicación web que requiera autenticación. En Express 5, podemos implementar un sistema de registro completo utilizando únicamente las capacidades nativas del framework, sin necesidad de librerías externas adicionales.

Para crear un endpoint de registro efectivo, necesitamos manejar la validación de datos, el almacenamiento temporal de usuarios y las respuestas HTTP apropiadas. Comenzaremos con una implementación básica que simule el almacenamiento en memoria.

Estructura básica del endpoint de registro

El endpoint de registro debe manejar peticiones POST y procesar los datos del usuario de forma segura:

import express from 'express';

const app = express();
const usuarios = []; // Almacenamiento temporal en memoria

// Middleware para parsear JSON
app.use(express.json());

app.post('/api/registro', (req, res) => {
  const { nombre, email, password } = req.body;
  
  // Validación básica de campos requeridos
  if (!nombre || !email || !password) {
    return res.status(400).json({
      error: 'Todos los campos son obligatorios',
      campos: ['nombre', 'email', 'password']
    });
  }
  
  // Verificar si el usuario ya existe
  const usuarioExistente = usuarios.find(user => user.email === email);
  if (usuarioExistente) {
    return res.status(409).json({
      error: 'El usuario ya está registrado',
      email: email
    });
  }
  
  // Crear nuevo usuario
  const nuevoUsuario = {
    id: usuarios.length + 1,
    nombre,
    email,
    password, // En producción esto debe estar hasheado
    fechaRegistro: new Date().toISOString()
  };
  
  usuarios.push(nuevoUsuario);
  
  // Respuesta exitosa (sin incluir la contraseña)
  const { password: _, ...usuarioRespuesta } = nuevoUsuario;
  res.status(201).json({
    mensaje: 'Usuario registrado exitosamente',
    usuario: usuarioRespuesta
  });
});

Validación avanzada de datos

Una implementación robusta requiere validaciones más específicas para garantizar la calidad de los datos:

// Función para validar formato de email
function validarEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

// Función para validar fortaleza de contraseña
function validarPassword(password) {
  return password.length >= 8 && 
         /[A-Z]/.test(password) && 
         /[a-z]/.test(password) && 
         /\d/.test(password);
}

app.post('/api/registro', (req, res) => {
  const { nombre, email, password } = req.body;
  
  // Validaciones específicas
  const errores = [];
  
  if (!nombre || nombre.trim().length < 2) {
    errores.push('El nombre debe tener al menos 2 caracteres');
  }
  
  if (!email || !validarEmail(email)) {
    errores.push('El email debe tener un formato válido');
  }
  
  if (!password || !validarPassword(password)) {
    errores.push('La contraseña debe tener al menos 8 caracteres, una mayúscula, una minúscula y un número');
  }
  
  if (errores.length > 0) {
    return res.status(400).json({
      error: 'Datos de registro inválidos',
      detalles: errores
    });
  }
  
  // Continuar con el registro...
});

Manejo de errores y respuestas consistentes

El manejo de errores debe ser consistente y proporcionar información útil tanto para el cliente como para el debugging:

// Middleware de manejo de errores para registro
function manejarErrorRegistro(error, req, res, next) {
  console.error('Error en registro:', error);
  
  if (error.type === 'entity.parse.failed') {
    return res.status(400).json({
      error: 'Formato JSON inválido',
      mensaje: 'Verifica la sintaxis del JSON enviado'
    });
  }
  
  res.status(500).json({
    error: 'Error interno del servidor',
    mensaje: 'No se pudo completar el registro'
  });
}

// Endpoint con manejo completo de errores
app.post('/api/registro', async (req, res, next) => {
  try {
    const { nombre, email, password } = req.body;
    
    // Normalizar datos de entrada
    const datosNormalizados = {
      nombre: nombre?.trim(),
      email: email?.toLowerCase().trim(),
      password: password
    };
    
    // Validaciones y registro...
    
    res.status(201).json({
      exito: true,
      mensaje: 'Usuario registrado correctamente',
      usuario: usuarioRespuesta
    });
    
  } catch (error) {
    next(error);
  }
});

app.use(manejarErrorRegistro);

Implementación con almacenamiento persistente simulado

Para simular un almacenamiento más realista, podemos implementar funciones que emulen operaciones de base de datos:

import fs from 'fs/promises';
import path from 'path';

const ARCHIVO_USUARIOS = path.join(process.cwd(), 'usuarios.json');

// Funciones de almacenamiento
async function cargarUsuarios() {
  try {
    const datos = await fs.readFile(ARCHIVO_USUARIOS, 'utf8');
    return JSON.parse(datos);
  } catch (error) {
    return []; // Si no existe el archivo, devolver array vacío
  }
}

async function guardarUsuarios(usuarios) {
  await fs.writeFile(ARCHIVO_USUARIOS, JSON.stringify(usuarios, null, 2));
}

async function buscarUsuarioPorEmail(email) {
  const usuarios = await cargarUsuarios();
  return usuarios.find(user => user.email === email);
}

// Endpoint con almacenamiento persistente
app.post('/api/registro', async (req, res, next) => {
  try {
    const { nombre, email, password } = req.body;
    
    // Verificar usuario existente
    const usuarioExistente = await buscarUsuarioPorEmail(email.toLowerCase());
    if (usuarioExistente) {
      return res.status(409).json({
        error: 'Email ya registrado',
        mensaje: 'Este email ya está asociado a una cuenta'
      });
    }
    
    // Crear nuevo usuario
    const usuarios = await cargarUsuarios();
    const nuevoUsuario = {
      id: Date.now(), // ID único basado en timestamp
      nombre: nombre.trim(),
      email: email.toLowerCase().trim(),
      password: password, // Temporal - se hasheará en la siguiente lección
      fechaRegistro: new Date().toISOString(),
      activo: true
    };
    
    usuarios.push(nuevoUsuario);
    await guardarUsuarios(usuarios);
    
    // Respuesta sin contraseña
    const { password: _, ...usuarioSeguro } = nuevoUsuario;
    res.status(201).json({
      exito: true,
      mensaje: 'Registro completado exitosamente',
      usuario: usuarioSeguro
    });
    
  } catch (error) {
    next(error);
  }
});

Middleware de validación reutilizable

Para mantener el código organizado y reutilizable, podemos crear middleware específico para validaciones:

// Middleware de validación de registro
function validarDatosRegistro(req, res, next) {
  const { nombre, email, password } = req.body;
  const errores = [];
  
  // Validar presencia de campos
  if (!nombre?.trim()) errores.push('Nombre es obligatorio');
  if (!email?.trim()) errores.push('Email es obligatorio');
  if (!password) errores.push('Contraseña es obligatoria');
  
  // Validar formatos
  if (email && !validarEmail(email)) {
    errores.push('Formato de email inválido');
  }
  
  if (password && !validarPassword(password)) {
    errores.push('Contraseña no cumple los requisitos de seguridad');
  }
  
  if (errores.length > 0) {
    return res.status(400).json({
      error: 'Datos de entrada inválidos',
      detalles: errores
    });
  }
  
  // Normalizar datos para el siguiente middleware
  req.datosRegistro = {
    nombre: nombre.trim(),
    email: email.toLowerCase().trim(),
    password: password
  };
  
  next();
}

// Uso del middleware
app.post('/api/registro', validarDatosRegistro, async (req, res, next) => {
  try {
    const { nombre, email, password } = req.datosRegistro;
    
    // Lógica de registro simplificada
    const usuarioExistente = await buscarUsuarioPorEmail(email);
    if (usuarioExistente) {
      return res.status(409).json({
        error: 'Usuario ya existe'
      });
    }
    
    // Crear y guardar usuario...
    
  } catch (error) {
    next(error);
  }
});

Esta implementación proporciona una base sólida para el registro de usuarios en Express 5, manejando validaciones, errores y almacenamiento de forma estructurada. El código está preparado para integrar el hashing de contraseñas en la siguiente lección, manteniendo la arquitectura limpia y escalable.

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 Registro de usuarios con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.