Introducción
Crear un método de registro para crear nuevos usuarios en el backend a nivel de base de datos.
Este método estará en un controlador y será accesible desde el frontend por cualquier usuario no autenticado.
Instalar librerías
Necesitaremos una librería para cifrar las contraseñas de forma segura antes de almacenarlas en la base de datos. bcrypt
es un estándar de la industria para esta tarea. También nos aseguraremos de tener class-validator
y class-transformer
para manejar la validación de los datos entrantes de forma declarativa.
npm install bcrypt class-validator class-transformer @nestjs/mapped-types
# o
yarn add bcrypt class-validator class-transformer @nestjs/mapped-types
Definición de la Entidad User
y el Role
Enum
Para gestionar nuestros usuarios, necesitamos una entidad User
que represente la tabla en la base de datos. También definiremos un Role
enum para asignar roles a los usuarios (por ejemplo, USER
por defecto).
src/user/role.enum.ts
export enum Role {
USER = 'user',
ADMIN = 'admin',
}
src/user/user.entity.ts
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { Role } from './role.enum'; // Asegúrate de que la ruta sea correcta
@Entity('users') // Opcional: especificar el nombre de la tabla
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
email: string;
@Column()
password: string;
@Column({ nullable: true })
phone: string;
@Column({ nullable: true })
addressStreet: string;
@Column({ nullable: true })
photoUrl: string;
@Column({
type: 'enum',
enum: Role,
default: Role.USER,
})
role: Role;
}
Creación del DTO (Data Transfer Object) para el Registro
Utilizaremos un DTO (Data Transfer Object) para definir la estructura de los datos que esperamos recibir del cliente al registrar un nuevo usuario. Esto nos permitirá validar los datos automáticamente gracias a class-validator
.
src/auth/dto/register.dto.ts
import { IsEmail, IsString, MinLength, IsNotEmpty, IsOptional } from 'class-validator';
export class RegisterDto {
@IsEmail({}, { message: 'El email debe ser una dirección de correo válida.' })
@IsNotEmpty({ message: 'El email es un campo requerido.' })
email: string;
@IsString({ message: 'La contraseña debe ser una cadena de texto.' })
@MinLength(6, { message: 'La contraseña debe tener al menos 6 caracteres.' })
@IsNotEmpty({ message: 'La contraseña es un campo requerido.' })
password?: string;
// Puedes añadir más campos opcionales si son parte de tu formulario de registro inicial
@IsOptional()
@IsString()
phone?: string;
@IsOptional()
@IsString()
addressStreet?: string;
@IsOptional()
@IsString()
photoUrl?: string;
}
Creación del Módulo AuthModule
Agruparemos la lógica de autenticación en su propio módulo AuthModule
. Este módulo importará TypeOrmModule.forFeature()
para registrar la entidad User
y hacer que su repositorio esté disponible para nuestros servicios y controladores de autenticación.
src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { User } from '../user/user.entity'; // Asegúrate de que la ruta sea correcta
@Module({
imports: [
TypeOrmModule.forFeature([User]), // Registra la entidad User para este módulo
],
controllers: [AuthController],
providers: [AuthService],
// Si AuthService será usado por otros módulos, puedes exportarlo:
// exports: [AuthService]
})
export class AuthModule {}
Desarrollo del Servicio AuthService
(Lógica de Negocio)
La lógica principal para el registro de usuarios (comprobar existencia, cifrar contraseña, guardar en DB) debe residir en un servicio. Esto mantiene tu controlador limpio y la lógica de negocio centralizada y reutilizable.
src/auth/auth.service.ts
Guarda tu progreso
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
import { Injectable, ConflictException, InternalServerErrorException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from '../user/user.entity';
import * as bcrypt from 'bcrypt'; // Importa bcrypt
import { RegisterDto } from './dto/register.dto';
import { Role } from '../user/role.enum';
@Injectable()
export class AuthService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {}
async register(registerDto: RegisterDto): Promise<User> {
const { email, password, ...userData } = registerDto;
// 1. Comprobar si el email ya está en uso
const existingUser = await this.userRepository.findOne({ where: { email } });
if (existingUser) {
throw new ConflictException('El email ya está registrado.'); // HTTP 409 Conflict
}
try {
// 2. Cifrar la contraseña
// El 'salt' (número de rondas de hash) recomendado es 10-12
const hashedPassword = await bcrypt.hash(password, 10);
// 3. Crear una nueva instancia de usuario
const newUser = this.userRepository.create({
email,
password: hashedPassword,
role: Role.USER, // Asignar rol por defecto
...userData // Añadir otros datos opcionales del DTO
});
// 4. Guardar el nuevo usuario en la base de datos
await this.userRepository.save(newUser);
// Opcional: eliminar la contraseña antes de devolver el objeto usuario
delete newUser.password;
return newUser;
} catch (error) {
// Manejo de errores más específico si es necesario (ej. problemas de DB)
console.error('Error al registrar usuario:', error);
throw new InternalServerErrorException('Error al registrar el usuario. Por favor, inténtelo de nuevo.');
}
}
}
Desarrollo del Controlador AuthController
El controlador AuthController
se encarga de recibir las peticiones HTTP y delegarlas al AuthService
. Aquí usaremos el decorador @Body()
con nuestro RegisterDto
.
src/auth/auth.controller.ts
import {
Controller,
Post,
Body,
HttpCode,
HttpStatus,
ConflictException,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { RegisterDto } from './dto/register.dto';
import { User } from '../user/user.entity';
@Controller('auth') // Prefijo de ruta para todos los endpoints de este controlador
export class AuthController {
constructor(private readonly authService: AuthService) { }
@Post('register')
@HttpCode(HttpStatus.CREATED) // Retorna un 201 Created si el registro es exitoso
@UsePipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }))
async register(@Body() registerDto: RegisterDto): Promise<User> {
return this.authService.register(registerDto);
}
}
Importar AuthModule
en AppModule
Para que NestJS reconozca y cargue nuestro AuthModule
, debemos importarlo en el AppModule
(nuestro módulo raíz).
src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthModule } from './auth/auth.module'; // Importa el AuthModule
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: '.env',
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: configService.get<'mysql'>('DB_TYPE'),
host: configService.get<string>('DB_HOST'),
port: configService.get<number>('DB_PORT'),
username: configService.get<string>('DB_USERNAME'),
password: configService.get<string>('DB_PASSWORD'),
database: configService.get<string>('DB_DATABASE'),
entities: [__dirname + '/**/*.entity{.ts,.js}'], // Esto detecta todas las entidades
synchronize: true,
}),
inject: [ConfigService],
}),
AuthModule, // Añade el AuthModule aquí
],
})
export class AppModule { }
Configuración de main.ts
para Validación Global
Para que la validación con DTOs funcione automáticamente en toda tu aplicación, debes configurar el ValidationPipe
globalmente en tu archivo main.ts
.
src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Habilita la validación global usando los DTOs y class-validator
app.useGlobalPipes(new ValidationPipe({
whitelist: true, // Remueve propiedades del cuerpo de la petición que no están definidas en el DTO
forbidNonWhitelisted: true, // Lanza un error si hay propiedades no definidas en el DTO
transform: true, // Transforma los payloads entrantes a instancias del DTO
}));
await app.listen(3000);
}
bootstrap();
Probando el Registro de Usuarios
Con esta implementación, puedes probar tu endpoint de registro utilizando herramientas como Postman o Insomnia.
- Ruta:
POST http://localhost:3000/auth/register
- Cuerpo de la Solicitud (JSON):
{
"email": "nuevo.usuario@example.com",
"password": "miPasswordSeguro123",
"phone": "123456789",
"addressStreet": "Calle Falsa 123"
}
- Si el registro es exitoso, recibirás un
201 Created
con los datos del nuevo usuario (sin la contraseña).
- Si intentas registrar el mismo email dos veces, recibirás un
409 Conflict
.
- Si envías datos inválidos (ej. email mal formado, contraseña corta), recibirás un
400 Bad Request
gracias aValidationPipe
.
- En tu BBDD si la solicitud ha sido exitosa, debería aparecer el nuevo registro:
Aprendizajes de esta lección
- Crear la entidad User
- Crear método para recibir datos de registro
- Verificar que el email a registrar no está ocupado
- Cifrar la contraseña
- Crear cuenta de usuario
Completa Nest y certifícate
Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs