nestjsx / crud

NestJs CRUD for RESTful APIs
https://github.com/nestjsx/crud/wiki
MIT License
4.09k stars 541 forks source link

500 - Internal Server Error on duplicate #460

Closed ymoreiratiti closed 4 years ago

ymoreiratiti commented 4 years ago

Hi.

My entity has a column with unique values. When i try to insert the same value, the api return "500 - Internal Server Error".

How can i catch and treat this response to anything more friendly? (Like an error "422 - Email already exists")


usuario.entity.ts

import { ApiProperty } from '@nestjs/swagger';
import { CrudValidationGroups } from '@nestjsx/crud';
import { IsBoolean, IsEmail, IsNotEmpty, IsOptional, IsString, MaxLength } from 'class-validator';
import { BeforeInsert, Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { PasswordTransformer } from './password.transformer';

const { CREATE, UPDATE } = CrudValidationGroups;

@Entity()
export class Usuario {
  @BeforeInsert()
  async hashPassword () {
    // this.password = await argon2.hash(this.password);
    console.log('this.password', this.password)
  }

  @ApiProperty()
  @PrimaryGeneratedColumn()
  id: number;

  @ApiProperty()
  @IsOptional({ groups: [UPDATE] })
  @IsNotEmpty({ groups: [CREATE] })
  @IsBoolean({ always: true })
  @Column({ type: 'boolean', default: true })
  ativo: boolean;

  @ApiProperty()
  @IsOptional({ groups: [UPDATE] })
  @IsNotEmpty({ groups: [CREATE] })
  @IsString({ always: true })
  @MaxLength(255, { always: true })
  @IsEmail({ require_tld: false }, { always: true })
  @Column({ type: 'varchar', length: 255, nullable: false, unique: true })
  email: string;

  @ApiProperty()
  @IsOptional({ groups: [UPDATE] })
  @IsNotEmpty({ groups: [CREATE] })
  @IsString({ always: true })
  @Column({ transformer: new PasswordTransformer() })
  password: string;

  @ApiProperty()
  @IsOptional({ groups: [UPDATE] })
  @IsNotEmpty({ groups: [CREATE] })
  @IsString({ always: true })
  @Column({ unique: true })
  cpfCnpj: string;

  @ApiProperty()
  @IsOptional({ groups: [UPDATE] })
  @IsNotEmpty({ groups: [CREATE] })
  @IsString({ always: true })
  @Column()
  fullUsername: string;
}

Return on shell


> api@0.0.1 start:dev C:\Temp\api
> nest start --watch

[21:18:24] Starting compilation in watch mode...

[21:18:29] Found 0 errors. Watching for file changes.

[Nest] 11104   - 2020-03-31 21:18:32   [NestFactory] Starting Nest application...
[Nest] 11104   - 2020-03-31 21:18:32   [InstanceLoader] TypeOrmModule dependencies initialized +175ms
[Nest] 11104   - 2020-03-31 21:18:32   [InstanceLoader] ConfigHostModule dependencies initialized +2ms
[Nest] 11104   - 2020-03-31 21:18:32   [InstanceLoader] AppModule dependencies initialized +0ms
[Nest] 11104   - 2020-03-31 21:18:32   [InstanceLoader] ConfigModule dependencies initialized +6ms
[Nest] 11104   - 2020-03-31 21:18:32   [InstanceLoader] TypeOrmCoreModule dependencies initialized +254ms
[Nest] 11104   - 2020-03-31 21:18:32   [InstanceLoader] TypeOrmModule dependencies initialized +1ms
[Nest] 11104   - 2020-03-31 21:18:32   [InstanceLoader] UsuarioModule dependencies initialized +2ms
[Nest] 11104   - 2020-03-31 21:18:32   [RoutesResolver] AppController {}: +167ms
[Nest] 11104   - 2020-03-31 21:18:32   [RouterExplorer] Mapped {, GET} route +8ms
[Nest] 11104   - 2020-03-31 21:18:32   [RoutesResolver] UsuarioController {/Usuarios}: +2ms
[Nest] 11104   - 2020-03-31 21:18:32   [RouterExplorer] Mapped {/Usuarios/:id, GET} route +1ms
[Nest] 11104   - 2020-03-31 21:18:32   [RouterExplorer] Mapped {/Usuarios, GET} route +1ms
[Nest] 11104   - 2020-03-31 21:18:32   [RouterExplorer] Mapped {/Usuarios, POST} route +1ms
[Nest] 11104   - 2020-03-31 21:18:32   [RouterExplorer] Mapped {/Usuarios/:id, PATCH} route +1ms
[Nest] 11104   - 2020-03-31 21:18:32   [RouterExplorer] Mapped {/Usuarios/:id, DELETE} route +1ms
[Nest] 11104   - 2020-03-31 21:18:32   [NestApplication] Nest application successfully started +2ms
this.password 123456
[Nest] 11104   - 2020-03-31 21:18:37   [ExceptionsHandler] duplicate key value violates unique constraint "UQ_2863682842e688ca198eb25c124" +4999ms
QueryFailedError: duplicate key value violates unique constraint "UQ_2863682842e688ca198eb25c124"
    at new QueryFailedError (C:\Temp\api\node_modules\typeorm\error\QueryFailedError.js:11:28)
    at Query.<anonymous> (C:\Temp\api\node_modules\typeorm\driver\postgres\PostgresQueryRunner.js:176:38)
    at Query.handleError (C:\Temp\api\node_modules\pg\lib\query.js:145:17)
    at Connection.connectedErrorMessageHandler (C:\Temp\api\node_modules\pg\lib\client.js:214:17)
    at Connection.emit (events.js:311:20)
    at Socket.<anonymous> (C:\Temp\api\node_modules\pg\lib\connection.js:134:12)
    at Socket.emit (events.js:311:20)
    at addChunk (_stream_readable.js:294:12)
    at readableAddChunk (_stream_readable.js:275:11)
    at Socket.Readable.push (_stream_readable.js:209:10)
michaelyali commented 4 years ago

You can use Exception Filters

moeldeeb1998 commented 7 months ago

In a Nest.js application, when you encounter a database error like a duplicate key violation while trying to add a new user with an existing email, you can catch this error and handle it appropriately. Assuming you're using an ORM like TypeORM, you can catch the QueryFailedError specifically for PostgreSQL databases.

Here's how you can handle the duplicate key violation error in your service or controller:

import { Injectable, UnprocessableEntityException} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, QueryFailedError } from 'typeorm';
import { CreateUserDto } from './dto/create-user.dto';
import { User } from './user.entity';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
  ) {}

  async createUser(createUserDto: CreateUserDto): Promise<User> {
    try {
      return await this.userRepository.save(createUserDto);
    } catch (error) {
      if (error instanceof QueryFailedError && error.message.includes('duplicate key value violates unique constraint')) {
        // Handle the duplicate key violation error
        throw new UnprocessableEntityException('Email already exists');
      } else {
        // For other types of errors, rethrow or handle them as needed
        throw error;
      }
    }
  }
}