项目配置
- 默认端口为3000,如果要修改可以在
src/main.ts
中进行修改
1 | 项目初始化 |
.env/dotenv配置文件支持
1 | npm i --save @nestjs/config |
Module模块
- 模块用于组织应用程序结构,用于创建controller和provider关系的
1 | @Global() // 一般不需要这个装饰器,除非要让一个模块变成全局模块,其他地方随时能使用,这个一般作用于helpers模块,这样其他模块想用就用,而不用在其他模块一个一个imports了 |
Provider提供者
- 例如service、repository、factory、helper等,都可以用来注入
资源Resource
- restful里面常用的概念
- 使用
nest g resource
能够直接生成一个资源对应的文件Module、Controller等,当然数据库model不会自动生成
Dto
- 用于前后端交互数据类型的定义,可以这样子将entity(model)转换为dto
1 | class MyDto { |
Entity
- 我们的model可以作为entity来用,以
*.entity.ts
结尾
路由与控制器
- 可以使用
nest g controller
生成控制器,不过最好还是用nest g resource
生成一个资源,包含了一些其他的逻辑文件
1 | @Controller() // 表示这是一个控制器 |
异常
1 | 常见异常,默认返回的是{"statusCode": 422, "error": "Unprocessable Entity"}格式 |
数据库
NestJs +Sequelize
安装:
1
2yarn add @nestjs/sequelize sequelize sequelize-typescript mysql2
yarn add -D @types/sequelizeMigration: 由于migration和代码无关,也无需依赖注入,可以直接用sequelize-cli命令来创建维护即可,参考Sequelize 使用手册
事务:官方不建议直接使用
@transaction
装饰器来包装事务,其实手动写也还好,因为需要用到事务的地方并不多配置,具体的数据表定义和用法可以参考sequelize-typescript文档以及我写的Sequelize 使用手册
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49// app.module.ts的imports中进行引入
@Module({
imports: [
SequelizeModule.forRoot({
dialect: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'test',
loggin: false, // 是否打印mysql的日志
models: [],
}),
forwardRef(() => AbcModule), // 如果两个module之间互相依赖,可以使用forwardRef来解决循环依赖的问题, can't resolve dependencies of the ...
],
})
// 通过nest g resource User来生成资源文件夹,然后在其目录下新建model文件,例如user.model.ts
import { Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript'
@Table({ tableName: 'users' })
export class UserModel extends Model {
@Column
username: string;
@CreatedAt
@Column({ field: 'created_at' })
createdAt: Date;
}
// 定义完成后需要在users.module.ts中引入该model
@Module({
imports: [
SequelizeModule.forFeature([UserModel])
],
controllers: [UsersController],
providers: [UsersService],
exports: [SequelizeModule]
})
export class UsersModule {}
// 然后就能在service注入了
@Injectable()
export class UsersService {
constructor (
@InjectModel(UserModel)
private readonly userModel: typeof UserModel
) {}
}
JWT认证Authentication
需要注意文档里的Enable authentication globally配置是全局的配置,我们一般不会需要这样做,因为登录注册等接口是不需要token的
在控制器获取jwt token的payload,可以这样做
1
2
3
4
5
6
7
8import {Request} from '@nestjs/common'
async getInfo(@Request() req: any) {
console.log(req.user);
return {
...req.user
};
}jwt-auth.guard.ts
中可以在handleRequest中处理错误1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext) {
// 程序首先就是在这里验证,如果这里返回false,那么会直接403
throw new UnauthorizedException()
const request = context.switchToHttp().getRequest()
request.user = await this.userService.findOne() // 可以这样,然后就可以在@Request req中使用了
return super.canActivate(context);
}
handleRequest(err, user, info) {
if (err || !user) {
throw err || new UnauthorizedException();
}
return user;
}
}
开启CORS
1 | const app = await NestFactory.create(AppModule, { cors: true }); |
OpenAPI/Swagger文档
- 官方文档: 按照官方文档安装以来,然后直接替换main.ts即可
1 | export class UserController { |
部署nestjs到aws lambda Serverless
官方文档虽然提了一下,但是并没有一个方便的包来实现这些步骤,只能按照他提供的步骤摸索着来。要将nestjs转换为serverless模式,最主要的问题就是减少冷启动的时间。所以我们最好在编译阶段就进行优化。
首先安装必要的依赖
1
2
3
4
5
6
7For npm
npm i @vendia/serverless-express aws-lambda
npm i -D @types/aws-lambda serverless-offline
For yarn
yarn add @vendia/serverless-express aws-lambda
yarn add -D @types/aws-lambda serverless-offline在根目录创建
serverless.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19service: serverless-example
plugins:
- serverless-offline
provider:
name: aws
runtime: nodejs20.x
functions:
main:
handler: dist/main.handler
events:
- http:
method: ANY
path: /
- http:
method: ANY
path: '{proxy+}'修改main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31import { NestFactory } from '@nestjs/core'
import { ValidationPipe, VersioningType } from '@nestjs/common'
import { AppModule } from './app.module'
import { CustomExceptionFilter } from './filter/exception.filter'
import { Callback, Context, Handler } from 'aws-lambda'
import serverlessExpress from '@vendia/serverless-express'
let server: Handler
async function bootstrap(): Promise<Handler> {
const app = await NestFactory.create(AppModule)
app.enableVersioning({
type: VersioningType.URI
})
app.useGlobalPipes(new ValidationPipe())
app.useGlobalFilters(new CustomExceptionFilter())
await app.init()
const expressApp = app.getHttpAdapter().getInstance()
return serverlessExpress({ app: expressApp })
}
export const handler: Handler = async (
event: any,
context: Context,
callback: Callback,
) => {
server = server ?? (await bootstrap())
return server(event, context, callback)
}在
tsconfig.json
中添加配置:1
2
3
4
5
6{
"compilerOptions": {
...
"esModuleInterop": true
}
}在根目录创建
webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30module.exports = (options, webpack) => {
const lazyImports = [
'@nestjs/microservices/microservices-module',
'@nestjs/websockets/socket-module',
]
return {
...options,
externals: [],
output: {
...options.output,
libraryTarget: 'commonjs2',
},
plugins: [
...options.plugins,
new webpack.IgnorePlugin({
checkResource(resource) {
if (lazyImports.includes(resource)) {
try {
require.resolve(resource)
} catch (err) {
return true
}
}
return false
},
}),
],
}
}测试
```shell这条命令会将整个目录编译为一个单独的js文件, dist/main.js
nest build –webpack
本地测试
npx serverless offline
部署,将该js文件压缩为zip包上传到aws lambda即可
常用扩展
nestjs-command
- 可用于编写命令行工具或者写一个daemon进程都可以,集成非常方便,直接复制文档中的例子即可
扩展文章
TroubleShooting
- 测试的时候报错Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test. 其他方法我试过不行,只能在
package.json
的jest
下添加"testTimeout": 60000