You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS搭配PostgreSQL创建用户前校验用户名是否存在的实现方法

NestJS 实现用户名重复校验方案

你用class-transformer找不到对应校验能力是正常的:这个库只负责普通对象与类实例的转换,不提供校验规则能力,重复名校验需要配合校验库+业务逻辑判断+数据库约束三层实现,从根本上避免重复用户创建。


第一步:安装依赖并开启全局校验

首先安装NestJS参数校验所需的依赖:

npm install class-validator class-transformer

在项目入口main.ts中全局开启参数校验管道,否则DTO上的校验规则不会生效:

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 全局启用参数校验
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(3000);
}
bootstrap();

第二步:添加数据库层唯一约束(核心兜底)

仅靠业务层查重无法覆盖高并发场景(两个请求同时判断用户名不存在、同时插入就会产生重复数据),必须先给username字段加数据库级唯一约束。
修改用户实体类:

// user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, Unique } from 'typeorm';

@Entity()
@Unique(['username']) // 给用户名字段加唯一索引约束
export class users_trans {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  password: string;
}

如果是手动维护表结构,执行如下SQL添加约束即可:

ALTER TABLE users_trans ADD CONSTRAINT uk_username UNIQUE (username);

第三步:编写创建用户的参数DTO

不要在Controller中直接用any接收请求参数,新建DTO类定义参数规则:

// create-user.dto.ts
import { IsNotEmpty, IsString } from 'class-validator';

export class CreateUserDto {
  @IsString()
  @IsNotEmpty({ message: '用户名不能为空' })
  username: string;

  @IsString()
  @IsNotEmpty({ message: '密码不能为空' })
  password: string;
}

第四步:修改Service层增加查重逻辑

在创建用户前先查询用户名是否存在,同时捕获数据库唯一约束冲突的错误做兜底处理:

// user.service.ts
import { Injectable, ConflictException } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { users_trans } from './users.entity';
import { CreateUserDto } from './create-user.dto';

@Injectable()
export class UsersService {
    constructor(@InjectRepository(users_trans) private usersRepo: Repository<users_trans>) {}

    findAll(): Promise<users_trans[]> {
        return this.usersRepo.find();
    }

    async create(newUser: CreateUserDto){
      // 业务层先查重
      const existUser = await this.usersRepo.findOne({
        where: { username: newUser.username }
      });
      if (existUser) {
        throw new ConflictException('用户名已存在,无法重复创建');
      }
      try {
        return await this.usersRepo.insert(newUser);
      } catch (err) {
        // PostgreSQL唯一约束冲突固定错误码为23505,兜底处理并发场景的重复请求
        if (err.code === '23505') {
          throw new ConflictException('用户名已存在,无法重复创建');
        }
        throw err;
      }
    }

    async update(usertoUpdate){
        return this.usersRepo.update(usertoUpdate.id,usertoUpdate);
    }

    async delete(id){
        return this.usersRepo.delete(id);
    }
}

第五步:修改Controller层用DTO接收参数

替换原有的any类型,让参数校验规则生效:

// user.controller.ts
import {UsersService} from './users.service';
import {users_trans} from './users.entity';
import { Controller, Get, Post, Body, Delete, Param, HttpCode } from '@nestjs/common';
import { CreateUserDto } from './create-user.dto';

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

    @Get('all')
    async getAll():Promise<users_trans[]>{
        return await this.usersService.findAll();
    }

    @Post('add')
    @HttpCode(201)
    async createUser(@Body() newUser: CreateUserDto){
        return await this.usersService.create(newUser);
    }

    @Post('update')
    @HttpCode(201)
    async updateUser(@Body() usertoUpdate:any){
        return this.usersService.update(usertoUpdate);
    }

    @Delete('delete/:id')
    @HttpCode(200)
    async deleteEmployee(@Param('id') id){
        return this.usersService.delete(id);
    }
}

注意点

  • 原有代码中数据库操作方法没有加await,会导致请求返回时数据库操作还未执行,异常也无法正常抛出给前端,上述代码已经补全异步写法。
  • 禁止省略数据库唯一约束,仅靠业务层查重在高并发下必然出现重复数据。

内容的提问来源于stack exchange,提问作者HardCodingFelix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 13:36:26