NestJS – Aprende como crear un CRUD completo

Hola a todos, hoy os voy a explicar como podemos crear un CRUD de forma sencilla en nestjs.

Un CRUD (Create Read Update Delete) nos ofrecen las operaciones básicas para gestionar un grupo de elementos ya sea en NestJS u otro sistema backend.

Creación de un proyecto en NestJS

Sino sabes como crear tu proyecto en NestJs, te dejamos un manual:

Como crear un proyecto con NestJS

Creación CRUD

Una vez creado nuestro proyecto, abrimos la terminal y escribimos el siguiente comando:

$ nest g resource users

Nos preguntará que capa de transporte usar, en nuestro caso elegiremos REST API

nestjs crud 1

Después, nos preguntará si queremos crear los entry points, esto lo veremos en el fichero controller, le decimos que si.

nestjs crud 2

Nos crea en la carpeta users con la siguiente estructura:

nestjs crud 3

El fichero user.controller.ts ya contiene los entry points que probaremos después.

import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  @Get()
  findAll() {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(+id, updateUserDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.usersService.remove(+id);
  }
}

El fichero user.module.ts ya contiene todo importado:

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';

@Module({
  controllers: [UsersController],
  providers: [UsersService]
})
export class UsersModule {}

El fichero user.service.ts le tenemos que dar nosotros la lógica, esto lo haremos después.

Entidades

La entidad a gestionar será usuarios, por lo que vamos a añadir propiedades a la clase de User del fichero user.entity.ts


export class User {
    // Identificador del usuario
    id: number;
    // Nombre del usuario
    name: string;
    // Email del usuario
    email: string;
}

Los dtos son clases que reciben los entry points en el controller, antes de tocar estas clases, vamos a instalar lo siguiente:

$ npm i class-validator class-transformer

La dependencia class-validator nos permite validar propiedades de nuestros dtos y class-transformer transforma un objeto a dto.

Veamos como validar un dto:

import { IsPositive, IsEmail, IsNotEmpty, IsOptional } from "class-validator";

export class CreateUserDto {
    
    @IsPositive() // Valido si es positivo
    @IsOptional() // No es obligatorio de incluir
    id?: number;
    
    @IsNotEmpty() // Es obligatorio de incluir
    name: string;
    
    @IsNotEmpty() // Es obligatorio de incluir
    @IsEmail() // Debe tener un formato de email
    email: string;
}

¿Con esto es suficiente? No, en el fichero main.ts, se debe agregar la siguiente linea, después de crear app, quedando asi:


import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Todos los endpoints seran validados
  app.useGlobalPipes(new ValidationPipe( { transform: true }))
  
  await app.listen(3000);
}
bootstrap();

Miremos la clase UpdateUserDto:


import { PartialType } from '@nestjs/mapped-types';
import { CreateUserDto } from './create-user.dto';

export class UpdateUserDto extends PartialType(CreateUserDto) {}

Ese extends PartialType(CreateUserDto) lo que hace es coger las propiedades del usuario y las convierte a todas en opcionales.

Dando lógica a nuestro servicio

Ya con todo esto que tenemos, podemos volver al fichero user.service.ts.

Crearemos un array de objetos que utilizaremos como mock y rellenar la lógica de las funciones. Ojo, esto es un ejemplo, puede variar según lo que se necesite.


import { ConflictException, Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User } from './entities/user.entity';

@Injectable()
export class UsersService {

  // Datos
  private users: User[] = [
    {
      id: 1,
      name: 'Fernando',
      email: 'fernando@gmail.com'
    },
    {
      id: 2,
      name: 'Alberto',
      email: 'alberto@gmail.com'
    },
    {
      id: 3,
      name: 'Miguel',
      email: 'miguel@gmail.com'
    },
  ];

  // Simulamos un autoincrement
  private autoIncrement: number = 4;

  /**
   * Crea un usuario
   * @param createUserDto 
   * @returns 
   */
  create(createUserDto: CreateUserDto) {
    
    // obtenemos el id que nos dan en createUserDto
    let id = createUserDto.id

    // sino existe el id, lo autoincrementamos
    if(!id){
      id = this.autoIncrement++;
    }else{
      // El id existe
      // Comprobamos si el id existe
      if(this.users.find(u => u.id == id)){
        throw new ConflictException('El id ya existe');
      }
      this.autoIncrement = id + 1;
    }

    // Comprobamos si el email existe
    if(this.users.find(u => u.email == createUserDto.email)){
      throw new ConflictException('El email ya existe');
    }

    // Creamos el usuario
    const user = {
      id,
      name: createUserDto.name,
      email: createUserDto.email
    };

    // añadimos el usuario al array
    this.users.push(user);

    // devolvemos el usuario
    return user;
  }

  /**
   * Devolvemos todos los usuarios
   * @returns 
   */
  findAll() {
    return this.users;
  }

  /**
   * Devolvemos un usuario dado un id
   * @param id 
   * @returns 
   */
  findOne(id: number) {
    return this.users.find(user => user.id == id);
  }

  /**
   * Actualizamos un usuario
   * @param id 
   * @param updateUserDto 
   * @returns 
   */
  update(id: number, updateUserDto: UpdateUserDto) {
    // Buscamos si el usuario existe
    let user = this.users.find(user => user.id == id);
    if(user){

      // Compruebo si el nuevo id existe, evitamos la comprobacion del propio usuario a editar
      if(updateUserDto.id && this.users.find(user => user.id != id && user.id == updateUserDto.id)){
        throw new ConflictException('El id ya existe');
      }

      // Compruebo si el nuevo email existe, evitamos la comprobacion del propio usuario a editar
      if(updateUserDto.email && this.users.find(user => user.id != id && user.email == updateUserDto.email)){
        throw new ConflictException('El email ya existe');
      }

      // Modificamos el id si viene en el objeto updateUserDto, sino le dejamos el que original
      user.id = updateUserDto.id ? updateUserDto.id : user.id;
      // Modificamos el name si viene en el objeto updateUserDto, sino le dejamos el que original
      user.name = updateUserDto.name ? updateUserDto.name : user.name;
      // Modificamos el email si viene en el objeto updateUserDto, sino le dejamos el que original
      user.email = updateUserDto.email ? updateUserDto.email : user.email;

      // Devuelve el usuario
      return user;
    }
    return null;
  }

  /**
   * Eliminamos un usuario dado un id
   * @param id 
   * @returns 
   */
  remove(id: number) {
    // Buscamos el id de un usuario
    let userIndex = this.users.findIndex(user => user.id == id);
    // Si existe lo borramos con splice
    if(userIndex != -1){
      this.users.splice(userIndex, 1);
      return true;
    }
    return false;
  }
}

Arrancar proyecto NestJS

Ahora, arrancamos el proyecto con el siguiente comando:

$ npm run start:dev

nestjs crud 4

Probar endpoints con Postman

Si pones http://localhost:3000 en el navegador, deberías ver esto:

Solo nos falta probar el resto de entry points, para ello, os recomiendo el programa postman, lo puedes descargar aquí.

Te dejo aquí un fichero con todos los entry points para importarlos. En la parte de arriba veras un botón para importarlos, solo arrastra el fichero y tendrás algo como esto:

nestjs crud 5

Probamos cada uno de ellos:

nestjs crud 6 nestjs crud 7 nestjs crud 8 nestjs crud 9 nestjs crud 10

Te lo dejo en video:

Te dejo un repositorio en github con todo lo visto.

Espero que os sea de ayuda. Si tenéis dudas, preguntad. Estamos para ayudarte.

Compartir

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *