nestJs

1. 简介

介绍 NestJS NestJS 是一个基于 TypeScript 的渐进式 Node.js 框架,它结合了 OOP、FP 和 FRP 的元素,以提供一种现代且可扩展的开发体验。NestJS 建立在 Express.js 之上,但是提供了更加抽象和模块化的方式来编写应用程序。NestJS 提供了许多关键功能,如依赖注入、面向切面编程、中间件、异常过滤、WebSocket、GraphQL 等。它还内置了支持多种数据库(如 MongoDB、MySQL、PostgreSQL)的 ORM 和 ODM 库,以及对测试的良好支持。

2. NestJS 的核心概念

模块化编程 NestJS 强调模块化编程思想,将各个模块分开以达到解耦的目的。

浅谈控制反转和依赖注入

  • 控制反转(IoC)是一种思想,其中一个典型的实现方式就是使用依赖注入(DI)
  • 什么是控制反转,顾名思义失去控制,反转控制权,主要是为了解耦组件之间的依赖关系,提高代码的可维护性和扩展性。 IoC指的是将控制流程交给框架或容器来管理,而不是在应用程序自己的代码中硬编码实现。这意味着应用程序不再需要自己创建和管理对象的生命周期,而是由框架或容器来负责。
  • 什么是依赖注入,DI指的是通过构造函数、属性或方法参数等方式,在组件创建时向其传递依赖的对象,也就是将组件所依赖的对象“注入”到组件中。这样组件就不需要自己创建和管理依赖的对象了,而是由其他组件或框架来负责创建和管理。这样可以降低组件之间的耦合度,同时也方便了单元测试和代码重用。

控制器和路由 控制器负责处理接收到的请求,并返回响应。路由是控制器的一部分,负责将请求映射到相应的控制器方法。

提供程序和依赖注入 提供程序是一个类或者值,可以被注入到控制器或其他提供程序中。依赖注入是指这些提供程序在运行时被动态注入到控制器或其他提供程序中。

中间件和异常处理 中间件是在请求和响应之间的操作,例如身份验证、日志记录等。异常处理是在捕获异常后处理异常的过程。

WebSocket 和 GraphQL NestJS 对 Websocket 和 GraphQL 也有丰富的支持,它们都可以通过 NestJS 的装饰器和模块来实现。

3. NestJS 开发基础

NestJS中文文档

npm i -g @nestjs/cli  // 全局安装Nest
nest new demo  // 创建项目

安装和配置 使用 npm 或者 yarn 可以安装 NestJS,并根据需要进行配置。

模块和模块之间的关系 NestJS 强调模块化编程思想,因此所有的功能都必须在模块内定义。

路由和控制器 路由是负责将请求映射到相应的控制器方法,而控制器则负责处理接收到的请求,并返回响应。 Nest.js中没有单独配置路由的地方,而是使用装饰器。Nest.js中定义了若干的装饰器用于处理路由。

提供程序和依赖注入 提供程序是一个类或者值,可以被注入到控制器或其他提供程序中。依赖注入是指这些提供程序在运行时被动态注入到控制器或其他提供程序中。

nestJS CLI 常用命令

# 生成controller.ts
 $nest g co user
# 生成  module.ts
 $nest g mo user
# 生成service.ts
 $nest g s user
# 直接使用一个命令生成CURD
 $nest g resource user

Nestjs上传oss nestjs项目_nest

4. NestJS 进阶开发

中间件的使用和自定义中间件 NestJS 内置了一些常用的中间件,例如 CORS、Body Parser 等,同时也支持自定义中间件。

异常处理和自定义异常 异常处理是在捕获异常后处理异常的过程。NestJS 内置了一些常见的异常类,也支持自定义异常。

WebSocket 的使用和实现 NestJS 可以很方便地支持 WebSocket,只需要使用相应的装饰器和模块即可。

GraphQL 和 RESTful API 的比较以及如何实现 GraphQL NestJS 对 GraphQL 有着良好的支持,可以使用它来替代传统的 RESTful API。

集成swagger

  • 安装swagger
yarn add @nestjs/swagger swagger-ui-express --save
import { NestFactory } from '@nestjs/core';
import { ParseIntPipe } from '@nestjs/common';
import { AppModule } from './app.module';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

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

  //全局使用管道:这里使用的是内置,也可以使用自定义管道,在下文
  app.useGlobalPipes(new ParseIntPipe());
  app.setGlobalPrefix('api');
  //全局使用中间件
  // app.use(LoggerMiddleware);

  // //全局使用过滤器
  // //这里使用的是自定义过滤器,先别管,先学会怎么在全局使用
  // app.useGlobalFilters(new HttpExceptionFilter());

  // //全局使用守卫
  // app.useGlobalGuards(new AuthGuard());

  // //全局使用拦截器
  // app.useGlobalInterceptors(new AuthInterceptor());
  const config = new DocumentBuilder()
    .setTitle('demo')
    .setDescription('nestJs项目的API使用文档')
    .setVersion('1.0.0')
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('/api', app, document);
  await app.listen(4010);
}
bootstrap();

集成TypeORM和MySql

为了开始使用它,我们首先安装所需的依赖项。在本章中,我们将演示如何使用流行的关系型数据库 Mysql , TypeORM 提供了对许多关系数据库的支持,比如 PostgreSQL 、Oracle、Microsoft SQL Server、SQLite,甚至像 MongoDB 这样的 NoSQL 数据库。

为了与 SQL和 NoSQL 数据库集成,Nest 提供了 @nestjs/typeorm 包。Nest 使用TypeORM是因为它是 TypeScript 中最成熟的对象关系映射器( ORM )。因为它是用 TypeScript 编写的,所以可以很好地与 Nest 框架集成。

  • 安装mysql以及navicat
  • 安装TypeORM
yarn add --save @nestjs/typeorm typeorm mysql2

官方提供了两种连接数据库的方法:

方法1

首先在项目根目录下创建两个文件.env和.env.prod,分别存的是开发环境和线上环境不同的环境变量:(.env.prod中的是上线要用的数据库信息,如果你的项目要上传到线上管理,为了安全性考虑,建议这个文件添加到.gitignore中)

// 数据库地址
DB_HOST=localhost  
// 数据库端口
DB_PORT=3306
// 数据库登录名
DB_USER=root
// 数据库登录密码
DB_PASSWD=root
// 数据库名字
DB_DATABASE=test

接着在根目录下创建一个文件夹config(与src同级),然后再创建一个env.ts用于根据不同环境读取相应的配置文件。

import * as fs from 'fs';
import * as path from 'path';
const isProd = process.env.NODE_ENV === 'production';
 
function parseEnv() {
  const localEnv = path.resolve('.env');
  const prodEnv = path.resolve('.env.prod');
 
  if (!fs.existsSync(localEnv) && !fs.existsSync(prodEnv)) {
    throw new Error('缺少环境配置文件');
  }
 
  const filePath = isProd && fs.existsSync(prodEnv) ? prodEnv : localEnv;
  return { path:filePath };
}
  • app.module.ts
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigService, ConfigModule } from '@nestjs/config';
import envConfig from './config/env';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // 设置为全局
      envFilePath: [envConfig.path],
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        type: 'mysql', // 数据库类型
        entities: [], // 数据表实体
        host: configService.get('DB_HOST', 'localhost'), // 主机,默认为localhost
        port: configService.get<number>('DB_PORT', 3306), // 端口号
        username: configService.get('DB_USER', 'root'), // 用户名
        password: configService.get('DB_PASSWORD', 'root'), // 密码
        database: configService.get('DB_DATABASE', 'test'), //数据库名
        timezone: '+08:00', //服务器上配置的时区
        synchronize: true, //根据实体自动创建数据库表, 生产环境建议关闭
      }),
    }),
    UsersModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

警告:设置 synchronize: true 不能被用于生产环境,否则您可能会丢失生产环境数据

方法二
  • app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'test',
      entities: ['dist/**/*.entity{.ts,.js}'],
      synchronize: false,
      autoLoadEntities: true,
    }),
  ],
})
export class AppModule {}

Nestjs上传oss nestjs项目_nest_02