前言

前一篇写NestJS的特点,使用NestJS,开启我们的Hello World! 以及NestJS各种热更新方法,本篇会写NestJS在实际项目中的应用。(ORM、参数校验、全局错误处理等等)

NestJS 中使用 TypeORM

一、安装依赖

 1npm install @nestjs/typeorm typeorm mysql2

这里假设使用 MySQL 数据库,你可以根据实际情况选择其他数据库驱动。

二、配置数据库连接

  1. 在项目根目录下创建一个文件 ormconfig.js,内容如下:
 1module.exports = {
 2    type: 'mysql',
 3    host: 'localhost',
 4    port: 3306,
 5    username: 'your_username',
 6    password: 'your_password',
 7    database: 'your_database_name',
 8    entities: [__dirname + '/**/*.entity{.ts,.js}'],
 9    synchronize: true, 
10};
11

将 your_usernameyour_password 和 your_database_name 替换为你的实际数据库连接信息。

  1. 在 main.ts 文件中引入并配置 TypeORM:
 1
 2  import { NestFactory } from '@nestjs/core';
 3  import { AppModule } from './app.module';
 4  import { TypeOrmModule } from '@nestjs/typeorm';
 5
 6  async function bootstrap() {
 7     const app = await NestFactory.create(AppModule);
 8     app.useGlobalPipes();
 9
10     await app.listen(3000);
11   }
12
13  bootstrap();
14

三、创建实体

在 src/entities 目录下创建一个实体文件,例如 user.entity.ts

 1import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; 
 2
 3@Entity() 
 4export class User { 
 5    @PrimaryGeneratedColumn() 
 6    id: number; 
 7    @Column() 
 8    name: string; 
 9    @Column() 
10    email: string; 
11}
12

四、创建数据访问层(Repository)

在 src/repositories 目录下创建一个文件,例如 user.repository.ts

 1import { Repository } from 'typeorm'; 
 2import { User } from '../entities/user.entity'; 
 3export class UserRepository extends Repository<User> {}

五、在模块中配置 TypeORM 和使用数据访问层

在对应的模块文件中,例如 app.module.ts

 1import { Module } from '@nestjs/common';
 2import { TypeOrmModule } from '@nestjs/typeorm';
 3import { UserEntity } from './entities/user.entity';
 4import { UserRepository } from './repositories/user.repository';
 5import { UsersService } from './services/users.service';
 6import { UsersController } from './controllers/users.controller';
 7
 8@Module({
 9  imports: [TypeOrmModule.forFeature([UserEntity])],
10  providers: [UsersService, UserRepository],
11  controllers: [UsersController],
12})
13export class AppModule {}

六、使用数据访问层进行数据库操作

在服务文件中注入数据访问层并进行数据库操作,例如在 users.service.ts

 1import { Injectable } from '@nestjs/common';
 2import { InjectRepository } from '@nestjs/typeorm';
 3import { User } from './entities/user.entity';
 4import { UserRepository } from './repositories/user.repository';
 5
 6@Injectable()
 7export class UsersService {
 8  constructor(
 9    @InjectRepository(UserRepository)
10    private readonly userRepository: UserRepository,
11  ) {}
12
13  async findAll(): Promise<User[]> {
14    return this.userRepository.find();
15  }
16
17  async create(user: User): Promise<User> {
18    return this.userRepository.save(user);
19  }
20}

这样就可以在 NestJS 项目中使用 TypeORM 进行数据库操作了。

NestJS中全局错误处理

一、创建全局错误过滤器

  1. 创建一个类来实现ExceptionFilter接口,用于处理全局错误。例如,创建一个文件global-exception.filter.ts
 1   import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
 2   import { Request, Response } from 'express';
 3
 4   @Catch()
 5   export class GlobalExceptionFilter implements ExceptionFilter {
 6     catch(exception: any, host: ArgumentsHost) {
 7       const ctx = host.switchToHttp();
 8       const response = ctx.getResponse<Response>();
 9       const request = ctx.getRequest<Request>();
10       const status = exception instanceof HttpException? exception.getStatus() : 500;
11       const message =
12         exception instanceof HttpException
13          ? exception.getResponse()['message'] || exception.message
14           : 'Internal server error';
15
16       response.status(status).json({
17         statusCode: status,
18         message,
19         timestamp: new Date().toISOString(),
20         path: request.url,
21       });
22     }
23   }

这个过滤器会捕获所有的异常,并返回一个统一格式的错误响应。

二、注册全局错误过滤器

  1. main.ts文件中注册全局错误过滤器:
 1   import { NestFactory } from '@nestjs/core';
 2   import { AppModule } from './app.module';
 3   import { GlobalExceptionFilter } from './global-exception.filter';
 4
 5   async function bootstrap() {
 6     const app = await NestFactory.create(AppModule);
 7     app.useGlobalFilters(new GlobalExceptionFilter());
 8     await app.listen(3000);
 9   }
10
11   bootstrap();

现在,所有在应用程序中抛出的异常都会被全局错误过滤器捕获,并返回统一格式的错误响应。你可以根据实际需求进一步扩展错误处理逻辑,例如记录错误日志、发送通知等。

错误日志

在 NestJS 中可以添加错误日志来记录应用程序中的错误信息,以便于调试和故障排查。以下是一种添加错误日志的方法:

一、安装日志库

可以使用winstonwinston-daily-rotate-file库来实现日志记录。

 1npm install winston winston-daily-rotate-file

二、创建日志服务

创建一个日志服务来处理日志记录。例如,创建一个文件logger.service.ts

 1import { Injectable, LoggerService as NestLoggerService } from '@nestjs/common';
 2import * as winston from 'winston';
 3import 'winston-daily-rotate-file';
 4
 5@Injectable()
 6export class LoggerService implements NestLoggerService {
 7  private logger: winston.Logger;
 8
 9  constructor() {
10    this.logger = winston.createLogger({
11      level: 'info',
12      format: winston.format.combine(
13        winston.format.timestamp(),
14        winston.format.json(),
15      ),
16      transports: [
17        new winston.transports.Console(),
18        new winston.transports.DailyRotateFile({
19          filename: 'logs/application-%DATE%.log',
20          datePattern: 'YYYY-MM-DD',
21          zippedArchive: true,
22          maxSize: '20m',
23          maxFiles: '14d',
24        }),
25      ],
26    });
27  }
28
29  log(message: string) {
30    this.logger.info(message);
31  }
32
33  error(message: string, trace: string) {
34    this.logger.error({ message, trace });
35  }
36
37  warn(message: string) {
38    this.logger.warn(message);
39  }
40
41  debug(message: string) {
42    this.logger.debug(message);
43  }

三、在全局错误过滤器中使用日志服务

修改全局错误过滤器,在捕获到错误时使用日志服务记录错误信息。文件global-exception.filter.ts

 1import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
 2import { Request, Response } from 'express';
 3import { LoggerService } from './logger.service';
 4
 5@Catch()
 6export class GlobalExceptionFilter implements ExceptionFilter {
 7  constructor(private readonly logger: LoggerService) {}
 8
 9  catch(exception: any, host: ArgumentsHost) {
10    const ctx = host.switchToHttp();
11    const response = ctx.getResponse<Response>();
12    const request = ctx.getRequest<Request>();
13    const status = exception instanceof HttpException? exception.getStatus() : 500;
14    const message =
15      exception instanceof HttpException
16       ? exception.getResponse()['message'] || exception.message
17        : 'Internal server error';
18
19    this.logger.error({
20      statusCode: status,
21      message,
22      timestamp: new Date().toISOString(),
23      path: request.url,
24    });
25
26    response.status(status).json({
27      statusCode: status,
28      message,
29      timestamp: new Date().toISOString(),
30      path: request.url,
31    });
32  }
33}

在 main.ts 中注册全局错误过滤器和日志服务:

 1   import { NestFactory } from '@nestjs/core';
 2   import { AppModule } from './app.module';
 3   import { GlobalExceptionFilter } from './global-exception.filter';
 4   import { LoggerService } from './logger.service';
 5
 6   async function bootstrap() {
 7     const app = await NestFactory.create(AppModule);
 8     app.useGlobalFilters(new GlobalExceptionFilter(app.get(LoggerService)));
 9     await app.listen(3000);
10   }
11
12   bootstrap();

四、在其他地方使用日志服务

可以在其他服务、控制器等地方注入日志服务,并使用它来记录日志信息。例如,在一个服务中:

 1import { Injectable } from '@nestjs/common';
 2import { LoggerService } from './logger.service';
 3
 4@Injectable()
 5export class SomeService {
 6  constructor(private readonly logger: LoggerService) {}
 7
 8  someMethod() {
 9    this.logger.log('This is a log message.');
10    
11    try {
12      
13    } catch (error) {
14      this.logger.error('An error occurred in someMethod', error.stack);
15    }
16  }
17}

这样,在应用程序中发生错误时,错误信息会被记录到日志文件中,同时也可以在其他地方使用日志服务记录各种信息,方便调试和故障排查。

NestJS参数校验 zod

因为之前koa用的Joi,想找一个类似的。zod 是一个强大的类型安全和数据验证库。 优点:

  • 提供了简洁直观的 API 来定义和验证数据结构。
  • 支持类型推导,使得类型安全更加可靠。

Joizod都是 JavaScript 和 TypeScript 中常用的用于数据验证和校验的库,它们有一些相似之处,但也存在一些不同点:

一、相似之处

  1. 数据验证功能

    • 两者都可以对输入数据进行各种类型的验证,包括字符串长度、数值范围、数据格式(如电子邮件地址)等。
    • 例如,都可以验证一个字符串是否为有效的电子邮件地址,或者一个数值是否在特定的范围内。
  2. 链式调用语法

    • Joizod都提供了一种链式调用的语法,使得可以方便地组合多个验证规则。
    • 例如,可以通过连续调用方法来添加多个验证条件,使代码更加清晰和易读。
 1import { z } from 'zod';
 2
 3const userSchema = z.object({
 4  name: z.string().min(3),
 5  email: z.string().email(),
 6});
 7
 8export class CreateUserDto {
 9  constructor(data: z.infer<typeof userSchema>) {
10    Object.assign(this, data);
11  }
12
13  name: string;
14  email: string;
15}
  1. 安装 zod 库:
 1   npm install zod
  1. 创建数据验证 schema:

    例如,创建一个用于用户创建的 schema:

 1   import { z } from 'zod';
 2
 3   const createUserSchema = z.object({
 4     name: z.string().min(3),
 5     email: z.string().email(),
 6   });
  1. 在控制器中使用:
 1   import { Controller, Post, Body } from '@nestjs/common';
 2   import { createUserSchema } from './schemas/user.schema';
 3
 4   @Controller('users')
 5   export class UserController {
 6     @Post()
 7     async createUser(@Body() body: any) {
 8       const parsedData = createUserSchema.safeParse(body);
 9       if (!parsedData.success) {
10         
11         return { error: parsedData.error };
12       }
13       
14       const { name, email } = parsedData.data;
15       
16       return { message: 'User created successfully' };
17     }
18   }

这样,当有 POST 请求到 /users 路径时,会使用 zod schema 对请求体进行校验,如果校验不通过,会返回校验错误信息。

后面把koa项目使用Nestjs重构完,就把代码开源。

参考:

nest.nodejs.cn

cloud.tencent.com/developer/a…

blog.csdn.net/weixin_4712…

cloud.tencent.com/developer/a…

个人笔记记录 2021 ~ 2025