🗨️ 问一下DeepSeek今天要学什么
安装与运行
(typescript)安装express的声明文件
运行(生产)
- 生产模式启动,适用于部署环境、测试生产环境的启动流程(是运行生产环境代码)
- 不监听文件变化
运行(开发)
加快构建速度(使用swc)
构建生产环境代码
术语表
解答
- 为什么说是集成express,因为例如请求、相应对象是相通的
学习日记
D1
- 创建一个模块
- 连接数据库
- 添加Swagger文档
- 错误处理
基础模块
创建一个user
模块(生成模块必须文件和可选的.spec.ts
的测试文件),可使用nest g --help
查看更多命令
- 一般选择
REST API
- 创建的模块会自动更新
app.module.ts
文件
- 此命令不会创建
dto
文件(用来规范客户端与服务端之间的数据格式),需手动创建
- 文件一般被放在如
user/dto/create-user.dto.ts
这里

编辑后的代码示例:
user.controller.ts
(控制器)接收请求,具体的业务逻辑由use.service.ts
文件提供
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import { Controller, Get, Post, Body } from '@nestjs/common'; import { UserService } from './user.service'; import { CreateUserDto } from './dto/create-user.dto';
@Controller('user') export class UserController { constructor(private readonly userService: UserService) {}
@Get() getUsers() { return this.userService.getUsers() }
@Post() createUser(@Body() createUserDto: CreateUserDto) { this.userService.createUser(createUserDto) } }
|
use.service.ts
(提供器、服务)处理更为复杂的业务需求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import { Injectable } from '@nestjs/common'; import { CreateUserDto } from './dto/create-user.dto';
@Injectable() export class UserService { private users = [ { id: 1, name: '小明', age: 18 }, { id: 2, name: '小白', age: 19 } ]
getUsers() { return this.users }
createUser(createUserDto: CreateUserDto) { const newUser = { id: Date.now(), ...createUserDto } this.users.push(newUser) return newUser } }
|
create-user.dto.ts
(Data Transfer Object、数据传输对象)可单独作为一个普通dto来使用
1 2 3 4
| export class CreateUserDto { name: string age: number }
|
还可以结合class-validator
(需要额外安装)来实现更高效的验证
1
| npm install class-validator class-transformer
|
1 2 3 4 5 6 7 8 9 10 11 12
| import { IsString, IsEmail, MinLength } from 'class-validator'
export class CreateUserDto { @IsString() @MinLength(2) name: string
age: number @IsEmail() email: string }
|
添加Swagger文档
1
| npm install @nestjs/swagger
|
修改main.ts
以配置swagger,访问文档http://localhost:3000/api
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
async function bootstrap() { const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder() .setTitle('NestJS学习') .setDescription('NestJS学习API文档') .setVersion('1.0') .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, document);
await app.listen(3000); }
|
在main.ts
文件中相当于初始化swagger,具体的接口描述可以使用swagger提供的装饰器在具体的接口中使用
1 2 3 4 5 6 7 8 9 10 11 12
| import { ApiTags } from '@nestjs/swagger';
@ApiTags('user') @Controller('user') export class UserController { constructor(private readonly userService: UserService) {}
@Get() getUsers() { return this.userService.getUsers() } }
|
错误处理
创建文件src/filters/http-exception.filter.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common'; import { Response } from 'express';
@Catch() export class HttpExceptionFilter implements ExceptionFilter { catch(exception: Error, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse<Response>();
response.status(500).json({ message: 'Internal Server Error', error: exception.message, }); } }
|
修改main.ts
以全局注册过滤器
1
| app.useGlobalFilters(new HttpExceptionFilter());
|
D2
- 用户认证(JWT)
- 中间件实战(日志级联)
- 环境配置
- 单元测试入门
- 部署优化(生产环境配置)
JWT
1 2
| npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt npm install @types/passport-jwt @types/bcrypt --save-dev
|
创建认证模块

实现用户注册(需要创建和数据库对应的entity),如:
src/user/entities/user.entity.ts
1 2 3 4 5
| export class User { id: number username: string password: string }
|
修改auth.service.ts
实现登录相关的业务逻辑(获取用户数据,实现成功登录token返回)
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
| import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt' import * as bcrypt from 'bcrypt' import { UserService } from '../user/user.service';
@Injectable() export class AuthService { constructor(private userService: UserService, private jwtService: JwtService ){}
async validateUser(username: string, pass: string): Promise<any> { const user = await this.userService.findOne(username) if (user && (await bcrypt.compare(pass, user.passwd))) { const { passwd, ...result} = user return result } return null }
async login(user: any) { const payload = { username: user.username, sub: user.id } return { access_token: this.jwtService.sign(payload) } } }
|
修改auth.controller.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { Controller, Body, Post, UnauthorizedException } from '@nestjs/common'; import { AuthService } from './auth.service';
@Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) {}
@Post('login') async login(@Body() loginDto: { username: string; password: string }) { const user = await this.authService.validateUser(loginDto.username, loginDto.password) if (!user) throw new UnauthorizedException(); return this.authService.login(user) } }
|
修改auth.module.ts
文件配置JWT模块
1 2 3 4 5 6 7 8 9 10 11 12
| import { JwtModule } from '@nestjs/jwt';
@Module({ imports: [ JwtModule.register({ secret: 'your_secret_key', signOptions: { expiresIn: '1h' }, }), ], providers: [AuthService], }) export class AuthModule {}
|
中间件
中间件一般会保存到如:
src/middleware/logger.middleware.ts
1 2 3 4 5 6 7 8 9 10
| import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express';
@Injectable() export class LoggerMiddleware implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`); next(); } }
|
修改mian.ts
全局应用中间件
1 2 3
| import { LoggerMiddleWare } from './middleware/logger.middleware';
app.use(LoggerMiddleware)
|
环境配置
通过.env
文件来区分不同环境
1
| npm install @nestjs/config
|
修改app.module.ts
进行配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config' import { AppController } from './app.controller'; import { AppService } from './app.service'; import { UserModule } from './user/user.module'; import { AuthModule } from './auth/auth.module';
@Module({ imports: [UserModule, AuthModule, ConfigModule.forRoot({ isGlobal: true, envFilePath: `.env.${process.env.NODE_ENV || 'development'}` })], controllers: [AppController], providers: [AppService], }) export class AppModule {}
|
本地创建两个不同的环境变量文件:.env.development
和 .env.production
1 2 3 4 5 6 7
| # .env.development DATABASE_URL=sqlite:./dev.db JWT_SECRET=development_secret
# .env.production DATABASE_URL=mysql://user:password@localhost:3306/prod_db JWT_SECRET=production_secret
|
单元测试
单元测试文件在运行nest g resource xx
的时候会自动创建,也可手动创建
例如测试UserService
,修改user.service.spec.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
| import { Test, TestingModule } from '@nestjs/testing'; import { UserService } from './user.service';
describe('UserService', () => { let service: UserService;
beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [UserService], }).compile();
service = module.get<UserService>(UserService); });
it('should be defined', () => { expect(service).toBeDefined(); });
it('show return users', () => { expect(service.getUsers()).toEqual([ { id: 1, name: '小明', age: 18, passwd: '123' }, { id: 2, name: '小白', age: 19, passwd: '123' } ]) }) });
|
运行测试
部署优化
运行npm run start
的时候已经是生产环境,除此之外还可以打包之后再部署(可使用swc、pm2等进行打包)
安装swc
1
| npm install --save-dev @swc/cli @swc/core
|
在项目根目录下创建.swc
文件,并且添加打包命令到 npm-script,如"build": "swc src -d dist"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { "jsc": { "parser": { "syntax": "typescript", "tsx": false, "decorators": true, "dynamicImport": true }, "target": "es2020", "keepClassNames": true, "transform": { "legacyDecorator": true, "decoratorMetadata": true } }, "module": { "type": "commonjs" } }
|
使用docker进行容器部署,在项目根目录创建Dockerfile
文件(必须大写开头)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| FROM node:16
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/main.js"]
|
构建镜像和运行容器
1 2 3 4 5
| # 构建 Docker 镜像 docker build -t my-nest-app .
# 运行 Docker 容器 docker run -p 3000:3000 my-nest-app
|
使用nginx进行反向代理(处理跨域的好方法),当访问yourdomain.com/api
时代理到本地的3000
端口
1 2 3 4 5 6 7 8 9 10 11
| server { listen 80; server_name yourdomain.com;
location /api { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
|