Nest.js 脚手架改造(一)环境

新坑 不一定填完

本次改造内容:

  • 目录结构
  • TSLint替换为ESLint
  • pre-commit约束
  • 环境变量

官方脚手架

1
2
npm i -g @nestjs/cli
nest new project-name

目录结构

nest的目录结构没有严格规定,以下是个人划分方法(src目录),

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
src
|--extends // 业务无关模块或服务相关模块
|--config
|--logger
|--schedule
|--providers // 贯穿从请求到返回的一系列东西
|--filter
|--interceptor
|--middleware
|--guard
|--pipe
|--module // 业务模块
|--moduleA
|--dto
|--schemas
|--interface
|--moduleA.service.ts
|--moduleA.controller.ts
|--moduleA.module.ts
|--utils // 工具
|--enums // 枚举
|--exceptions // 异常

TSLint替换为ESLint

为什么要替换?因为微软不打算搞了:https://medium.com/palantir/tslint-in-2019-1a144c2317a9

删除TSLint

首先删除package.json中关于lint的部分

  • devDependencies: 删除tslint包
  • scripts: 删除lint脚本

然后删除根目录下的tslint.json

安装及配置ESLint

1
2
3
4
5
6
7
npm i
eslint \
@typescript-eslint/parser \
@typescript-eslint/eslint-plugin \
prettier \
eslint-config-prettiereslint-plugin-prettier \
--save-dev

prettier可以选择不要,个人习惯了,基本上每个项目都在用

然后在根目录下新建.eslintrc.js.prettierrc(可选)

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
// .eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended'
],
plugins: ['@typescript-eslint'],
rules: {
// 这条规则是为了防止写class interface的member时,分隔符和prettier产生冲突
'@typescript-eslint/member-delimiter-style': [
'error',
{
multiline: {
delimiter: 'none',
requireLast: false
},
singleline: {
delimiter: 'comma',
requireLast: false
}
}
]
}
}

// .prettierrc
{
"singleQuote": true,
"semi": false
}

根据需要可以自行添加规则

pre-commit约束

添加pre-commit的钩子,保证风格规范统一

npm i husky lint-staged --save-dev

1
2
3
4
5
6
7
8
9
10
11
12
13
// package.json
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,ts}": [
"prettier --write",
"eslint --fix",
"git add"
]
}

环境变量

环境变量这块用的官方,使用env文件+module的方式,

1
2
npm i --save dotenv
npm i --save-dev @types/dotenv

新建src/config/config.service.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import * as dotenv from 'dotenv'
import * as fs from 'fs'

export class ConfigService {
private readonly envConfig: { [key: string]: string }

constructor(filePath: string) {
this.envConfig = dotenv.parse(fs.readFileSync(filePath))
}

get(key: string): string {
return this.envConfig[key]
}
}

新建src/config/config.module.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { Module, Global } from '@nestjs/common'
import { ConfigService } from './config.service'

@Global()
@Module({
providers: [
{
provide: ConfigService,
useValue: new ConfigService(`${process.env.NODE_ENV}.env`)
}
],
exports: [ConfigService]
})
export class ConfigModule {}

最后在app.module.ts中注册

1
2
3
4
5
6
7
import { Module } from '@nestjs/common'
import { ConfigModule } from './config/config.module'

@Module({
imports: [ConfigModule]
})
export class AppModule {}

使用

现在根目录下有个development.env文件

1
2
USER=TEST
PORT=6000

安装cross-env

1
npm i cross-env --save-dev

然后修改package.jsonscripts

1
"start:dev": "cross-env NODE_ENV=development ..."

生产环境打算build之后用pm2,暂时不修改

由于ConfigModule被包装成了全局模块,在AppModule注册过就能在别的地方直接使用。

假设有个user.service.ts

1
2
3
4
5
6
7
8
9
10
11
import { Injectable } from '@nestjs/common'
import { ConfigService } from '../../config/config.service.ts'

export class UserService {
constructor(private readonly configService: ConfigService) {}

async findOne(): Promise<string> {
const user = this.configService.get('USER')
return user
}
}

如果要在main.ts中使用的话:

1
2
3
4
5
6
7
8
9
10
11
12
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { ConfigService } from './config/config.service'

async function bootstrap(): Promise<void> {
const app = await NestFactory.create(AppModule)
const configService = app.get(ConfigService)
const port = configService.get('PORT')

await app.listen(port)
}
bootstrap()

当然这是最简单的环境变量设置方法,如果需要智能提示、类型、校验等功能,官方有结合Joi的方法。