Skip to content

Nest的模块Module

@Module装饰器

@Module装饰器接受一个对象参数,在Nest源码中是ModuleMetadata接口,它有四个字段,且均是数组类型,分别是:

  • imports :导入其他模块中导出的Providers,以实现共享
  • providers :模块中所有用到的功能类,模块内共享实用;
  • controllers:控制器
  • exports:导出其他模块需要共享的Providers

通过以上四种类型的设定,Nest的IoC才能够准确识别需要组装(注入和被注入)各种依赖关系,同时实现一定程度的共享。

ts
import {Module} from "@nestjs/common"

@Module({
    imports:[], // 导入模块
    provider:[], // 注入服务
    controller:[], // 导入控制器
    exports: [], // 导出服务,这里导出后,其他模块的服务可以使用当前模块的服务。
  })

export class UserModule {}

异常拦截器

创建

ts
// http-exceptopn.ts
import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
} from '@nestjs/common';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: any, host: ArgumentsHost) {
    // 获取请求的上下文
    const ctx = host.switchToHttp();
    // 获取请求上下文的response对象
    const response = ctx.getResponse();
    // 获取异常的状态码
    const status = exception.getStatus();
    const msg = exception.message ? exception.message : '请求失败';

    console.log('status:', status);

    const errorResponse = {
      data: null,
      code: -1,
      msg,
    };

    response.status(status);
    response.header('Content-Type', 'application/json; charset=utf-8');
    response.send(errorResponse);
  }
}

使用

注册为全局异常拦截器。

ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './filter/http-exception';

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

  app.useGlobalFilters(new HttpExceptionFilter());

  await app.listen(3001);
}
bootstrap();

MYSQL数据库

运行MYSQL

bash
# 如果尚未运行MySQL容器,使用此命令启动(推荐数据持久化)
docker run -d --name mysql-server \
  -e MYSQL_ROOT_PASSWORD=your_password \
  -p 3306:3306 \
  -v mysql_data:/var/lib/mysql \
  mysql:8.0

# 验证容器状态
docker ps | grep mysql

登录MYSQL控制台

bash
# 方式一:直接进入容器内的 MySQL 命令行
docker exec -it mysql-server mysql -u root -p
# 输入预设的密码(即your_password)

# 方式二:通过主机客户端连接(需要安装mysql-client)
mysql -h 127.0.0.1 -P 3306 -u root -p

创建数据库

在 MySQL 命令行中执行:

bash
-- 创建数据库
CREATE DATABASE demo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 验证创建结果
SHOW DATABASES;

-- 退出命令行
exit

推荐的生产环境配置

bash
docker run -d \
  --name mysql-prod \
  --restart always \
  -p 3306:3306 \
  -v /path/to/mysql_data:/var/lib/mysql \
  -v /path/to/custom.cnf:/etc/mysql/conf.d/custom.cnf \
  -e MYSQL_ROOT_PASSWORD=strong_password \
  -e MYSQL_DATABASE=app_db \
  -e MYSQL_USER=app_user \
  -e MYSQL_PASSWORD=user_password \
  mysql:8.0 \
  --character-set-server=utf8mb4 \
  --collation-server=utf8mb4_unicode_ci

TypeORM的使用

TypeORM的两种使用方式

全局数据库实体初始化

在这一步中,无论使用下面的 那种方法,必须在这里初始化数据库,注册实体。

ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserModule } from './user/user.module';
import { User, Role, Permission } from './user/entities/index.entity';
import { JwtModule } from '@nestjs/jwt';
import { HttpExceptionFilter } from './filters/http-exception.filter';
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
import { TransformInterceptor } from './interceptors/transform.interceptor';

@Module({
  imports: [
    // 数据库配置
    TypeOrmModule.forRoot({
      // 数据库类型
      type: 'mysql',
      // 主机地址
      host: '119.96.24.5',
      // 数据库端口
      port: 3306,
      // 用户名
      username: 'root',
      // 数据库连接密码
      password: 'Xianyu@666',
      // 连接的数据库
      database: 'meetingroom_book_mysql_test',
      // 是否将实体和数据库同步
      synchronize: true,
      // 禁用日志输出
      logging: false,
      // 此处的实体是作为数据库初始化的需要,不管在后面的子模块怎么引入使用,这里必须先倒入注册
      entities: [User, Role, Permission],
      poolSize: 10,
      connectorPackage: 'mysql2',
      extra: {
        // 这里配置连接数据库的额外参数
        authPlugins: ['sha256_password'],
      },
    }),
    // 全局的jwt配置
    JwtModule.register({
      // 设置为全局模块,就不需要在每一个模块都导入了
      global: true,
      secret: 'chengang_test1',
      signOptions: {
        expiresIn: '1m',
      },
    }),
    UserModule,
  ],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_FILTER,
      useClass: HttpExceptionFilter,
    },
    {
      provide: APP_INTERCEPTOR,
      useClass: TransformInterceptor,
    },
  ],
})
export class AppModule {}

只在app.module.ts中导入实体

user.service.ts

这种方法的使用特点是每次都需要this.manager.xxx方法(xxx实体, xxx参数)来使用

ts
import { Injectable } from '@nestjs/common';
import { InjectEntityManager } from '@nestjs/typeorm';
import { EntityManager } from 'typeorm';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User } from './entities/user.entity';

@Injectable()
export class UserService {
  
  @InjectEntityManager()
  private manager: EntityManager;

  create(createUserDto: CreateUserDto) {
    this.manager.save(User, createUserDto);
  }

  findAll() {
    return this.manager.find(User)
  }

  findOne(id: number) {
    return this.manager.findOne(User, {
      where: { id }
    })
  }

  update(id: number, updateUserDto: UpdateUserDto) {
    this.manager.save(User, {
      id: id,
      ...updateUserDto
    })
  }

  remove(id: number) {
    this.manager.delete(User, id);
  }
}

在当前的模块xxx.module.ts中导入实体

1.需要现在xxx.module.ts中导入实体,示例如下:

ts
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';

@Module({
  imports: [
    TypeOrmModule.forFeature([User])
  ],
  controllers: [UserController],
  providers: [UserService]
})
export class UserModule {}

2.1.在service文件中注入使用如下:

ts
import { Injectable } from '@nestjs/common';
import { User } from './entities/index.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDto } from './dto/create-user.dto';

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

  async login(loginIfo: CreateUserDto) {
    const user = await this.userRepository.findOne({
      where: {
        username: loginIfo.username,
      },
      relations: {
        roles: true,
      },
    });
    if (!user) {
      return '用户不存在';
    }
    if (user.password !== loginIfo.password) {
      return '密码错误';
    }
    return user;
  }

  async createUser(createUserDto: CreateUserDto) {
    const findUser = await this.userRepository.findOneBy({ username: createUserDto.username });
    console.log(findUser);
    return findUser;
  }
}

2.2.在controller中注入使用如下:

ts
import { Repository } from 'typeorm';
import { User } from './user.entity';
import { UserService } from './user.service';
import { Body, Controller, Get, Inject, Logger, Post } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';

@Controller('user')
export class UserController {

    constructor(
        private readonly userService: UserService,
        @InjectRepository(User)
        private readonly usersRepository: Repository<User>,
    ) { }

    @Post("add")
    async add(@Body() user: User) {
        let result = await this.usersRepository.save(user);
        Logger.debug('添加用户', result, this.constructor.name);
        return "OK";
    }

    @Get("list")
    async list() {
        let result = await this.usersRepository.find();
        Logger.debug('查询用户', result, this.constructor.name);
        return result;
    }
}

TypeORM的查询

查询指定的字段

  • find 方法适合查询全部的数据
  • findAndCount 方法用来查询分页分页数据和总条数数据
ts
return await this.manager.find(User, {
  // 精准查询
  where: { id: 1 },
});

// 给定范围的查询
return await this.manager.find(User, {
  where: { id: MoreThan(10) },
});

Nestjs操作GitLab的API

Gitbeaker 是一款强大的开源项目,专为开发者设计,提供了全面的 GitLab SDK 支持,可用于浏览器、Node.js 和 Deno 环境,甚至包括命令行接口(CLI)。该项目不仅涵盖了GitLab的众多API功能,而且注重性能和可测试性,尤其对于使用TypeScript的团队来说,它的类型定义更是增添了开发的便捷性。

gitbeaker 由多个包组成,分别负责不同的任务:

  • @gitbeaker/requester-utils - 提供底层HTTP请求处理的工具。
  • @gitbeaker/core - 核心API,详细列出所有Gitlab资源的支持情况。
  • @gitbeaker/rest - 用于Node.js、Deno和现代浏览器的SDK,基于原生fetch进行封装。
  • @gitbeaker/cli - 命令行工具,方便直接与Gitbeaker交互。

具体的API演示

ts
return this.gitlabInstance.Users.create({
  // 用户的邮箱,必须要
  email: '13545630123@qq.com',
  password: '13545630123',
  username: '13545630123',
  // 默认新建的都不设置为最高权限的管理员
  admin: false,
  name: '13545630123',
  // 跳转新建用户之后的用户邮件验证确认操作
  skipConfirmation: true,
});