安装脚手架
npm install -g @nestjs/cli
nest new projectName
项目目录创建
删除 src 中多余的文件只保留 app.module.ts
和 main.ts
重置 app.module.ts 文件
import { Module } from '@nestjs/common';
@Module({
imports: [],
controllers: [],
providers: [],
})
export class AppModule {}
创建相关模块
nest g module user /*创建user的module*/
nest g controller User --no-spec /*创建 controller 层,忽略测试文件*/
nest g service user --no-spec /*创建 service 层,忽略测试文件*/
nest g resource use --no-spec /*创建模版(三合一),忽略测试文件*/
module 、controller 和 service 三者的关系
- controller (控制器):负责处理HTTP请求和响应,接受来自客户端的请求,并将其委派给 service 进行处理。controller 中定义了路由和处理请求的方式
- Module(模块):应用程序的核心组织单元,负责将应用程序的不同部分组合在一起,每一个 Module 都有自己的 Controller、Service 和 其他相关组件,他们整体构成了一个模块
- Service (服务):应用程序的业务逻辑集中地,他包含了处理业务逻辑的方法,Service 被 Controller 调用,用于处理来自客户端的请求,并返回结果,Service 可以被多个 COntroller 和其他的 Service 共享
Nest提供了结构化的方式来组织应用程序,将不同的功能模块分开管理
接口的版本控制
main.ts
中配置
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableVersioning({ /*配置版本号*/
type:VersioningType.URI
})
await app.listen(3000);
}
在文件的 controller
文件设置套接口的版本号
@Controller({
path:'user',
version:'1'
})
export class UserController{
...
}
/**在这某个接口的版本号*/
@Controller('user')
export class UserController{
@Get()
@Version("1")
findAll() {
return this.userService.findAll();
}
}
访问路径 localhost:3000/v1/user
控制器–接受get和post参数
常用控制器
控制器 | 描述 |
---|---|
@Request() | 请求的全部信息 |
@Query() | 查询字符串信息 |
@Body() | 请求体信息 |
@Header() | 获取请求头 |
@HttpCode() | 设置接口状态码 |
用法示例
@Post(":id")
@HttpCode(200)
postFun(
@Requery() req,
@Query() query,
@Body() body,
@Header() header,
@Param("id") id
){
console.log(req) /*里面包含了query,body,header,params 的所有参数*/
console.log(query) /*只获取query*/
console.log(body) /*只获取body*/
console.log(header)/*只获取header*/
console.log(id) /*只获取模版参数*/
reurn {code:101}
}
设置session
安装
yarn add express-session @types/express-session
配置 main.ts
app.use(session({
secret:"demo", /*签名[自定义]*/
rolling:true, /*强制设置cookie,重置过期时间*/
name:"demo.sid", /*session的字段名称*/
cookie:{maxAge:999999} /*过期的秒数*/
}))
在 controller
中使用 session
@Get()
getUser(@Session() session){
session.name = "张三" /*设置session*/
return {code:101}
}
@Get()
getSession(@Session session){
console.log(session.name) /*打印session*/
return {code:101}
}
模块 @Module
共享模块
如果 a.controller.ts
想使用 b.service.ts
的通用逻辑时,要在 b.module.ts
中导出该 Service 类, 在这里配置 b.module.ts
@Module({
...
exports:[BService]/*导出BService类*/
})
在a.controller.ts
直接引用
import {BService} from "b.service";
import { Controller, Get } from '@nestjs/common';
@Controller()
export class AController {
constructor(
private readonly aScrvice: AScrvice,
private readonly bService:UserService
) {}
@Get()
getHello(): string {
return this.bService.findAll();
}
}
中间件
路由处理程序之前调用的程序,中间件函数可以访问请求对象和响应对象(类似于axios的拦截器)
解决跨域问题
安装
yarn add cors @types/cors
配置 main.ts
import * as cors from "cors";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(cors())
....
await app.listen(3000);
}
文件上传
安装
yarn add multer @types/multer
在 upload.module.ts
指定存储路径
@Module({
imports:[
MulterModule.register({
storage:diskStorage({
destination:join("./public/uploaded"), /**存放目录 */
filename:(_,file,callback) => { /*时间戳定义文件名*/
const fileName = `${new Date().getTime() + extname(file.originalname)}`
return callback(null,fileName)
}
})
})
]
})
接口端 upload.controller.ts
@Controller('upload')
export class UploadController {
constructor(private readonly uploadService: UploadService) {}
@Post()
@UseInterceptors(FilesInterceptor("files"))
create(@UploadedFiles() files) {
console.log(files)
return {code:101}
}
}
给 public/uploaded
文件配置静态资源,供外部访问,在upload.module.ts
配置
@Module({
imports:[
...
ServeStaticModule.forRoot({
rootPath: join('./', 'public/uploaded'),
})
]
})
文件流下载
安装
yarn add compressing
接口层
@Get("steamDown")
async steamDown(@Res() res){
const url = join("./public/uploaded");
const tarStream = new zip.Stream();
await tarStream.addEntry(url);
res.setHeader("Content-Type","application/octet-stream")
res.setHeader("Content-Disposition","attachment; filename=xxx")
tarStream.pipe(res)
}
响应拦截器
新建 common / response.ts
import { CallHandler, Injectable, NestInterceptor } from "@nestjs/common";
import { map } from "rxjs";
@Injectable()
export class Response<T> implements NestInterceptor {
intercept(context,next:CallHandler){
return next.handle().pipe(map(data => ({
data,
message:"nibi"
})));
}
}
在全局注入 main.ts
import {Response} from "./common/response"
...
app.useGlobalInterceptors(new Response())
...
异常拦截器
新建 common \ filter.ts
import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from "@nestjs/common";
import {Request,Response} from "express"
@Catch()
export class HttpFilter implements ExceptionFilter {
catch(exception:HttpException,host:ArgumentsHost){
const ctx = host.switchToHttp()
const request = ctx.getRequest<Request>()
const response = ctx.getResponse<Response>()
response.status(500).json({
success:false,
time:new Date(),
data:exception.message,
path:request.url
})
}
}
全局注册拦截器
import { HttpFilter } from './common/filter';
...
app.useGlobalFilters(new HttpFilter())
...
管道
守卫
在中间件之后,管道和拦截器之前执行
创建命令
nest g gu role
在路由中使用 xxx.controller.ts
import {RoleGuard} from "../role/role.guard"
@Controller('guard')
@UseGuards(RoleGuard)
export class GuardController{...}
全局注册守卫
import {RoleGuard} from "./role/role.guard"
...
app.useGlobalGuards(new RoleGuard())
...
Swagger
安装
yarn add @nestjs/swagger swagger-ui-express
在 main.ts
配置标题等相关操作
async function bootstrap(){
...
const options = new DocumentBuilder().addBearerAuth().setTitle("标题").setDescription("描述").setVersion("1").build()
const document = SwaggerModule.createDocument(app,options)
SwaggerModule.setup("/api-docs",app,document)
...
await app.listen(3000);
}
在路由层分组,设置每个接口 xxx.controller.ts
/*路由层*/
@ApiTags("接口分组标题")
@ApiBearerAuth() /*是否携带token*/
/*接口层配置*/
@ApiOperation({summary:"get接口",description:'接口的功能描述'})
@ApiParam({name:"id",description:"要传递参数表述",required:true})
@ApiQuery({name:"page",description:"要传递的参数表述"})
@ApiResponse({status:403,description:"返回示例"})
/*Post请求需要再 dto 文件中配置*/
import { ApiProperty } from "@nestjs/swagger";
export class XxxDto {
@ApiProperty({example:'xxx'}) /*默认值*/
name:string; /*参数:类型*/
@ApiProperty({example:0})
age:number
}
连接数据库
安装
yarn add typeorm mysql2 @nestjs/typeorm
在 app.module.ts
中配置
@Module({
...
imports:[
TypeOrmModule.forRoot({
type: "mysql", //数据库类型
username: "root", //账号
password: "", //密码
host: "localhost", //host
port: 3306, //
database: "nestDB", //库名
//entities: [__dirname + '/**/*.entity{.ts,.js}'], //实体文件
synchronize:true, //synchronize字段代表是否自动将实体类同步到数据库(生产环境关闭)
retryDelay:500, //重试连接数据库间隔
retryAttempts:10,//重试连接数据库的次数
autoLoadEntities:true, //如果为true,将自动加载实体 forFeature()方法注册的每个实体都将自动添加到配置对象的实体数组中
}),
...
],
...
})
创建表实体
在 entity 文件中创建实体
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity()
export class User{ ... }
在 mudule 中创建关联
@Module({
imports:[TypeOrmModule.forFeature([User])],
...
})
自增的主键
@PrimaryGeneratedColumn()
id:number
自增uuid
@PrimaryGeneratedColumn("uuid")
id:number
列类型
@Column({type:"varchar",length:200}) /*可以调整数据库数据类型*/
password: string
@Column({ type: "int"})
age: number
@CreateDateColumn({type:"timestamp"}) /*自动存个时间戳*/
create_time:Date
自动生成一列
@Generated('uuid')
uuid:string
列选项
@Column({
type:"varchar",
name:"ipaaa", //数据库表中的列名
nullable:true, //在数据库中使列NULL或NOT NULL。 默认情况下,列是nullable:false
comment:"注释",
select:true, //定义在进行查询时是否默认隐藏此列。 设置为false时,列数据不会显示标准查询。 默认情况下,列是select:true
default:"xxxx", //加数据库级列的DEFAULT值
primary:false, //将列标记为主要列。 使用方式和@ PrimaryColumn相同。
update:true, //指示"save"操作是否更新列值。如果为false,则只能在第一次插入对象时编写该值。 默认值为"true"
collation:"", //定义列排序规则。
})
ip:string
typeorm对数据库的增删改查
*.service .ts
层用于业务处理
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './entities/user.entity'; /*需要引入定义好的 entity*/
import { Repository,Like } from 'typeorm';
@Injectable()
export class UserService{
constructor(@InjectRepository(User) private readonly user:Repository<User>){}
创建(){
const data = new User()
data.name = "张三"
data.doc = "法外狂徒"
const result = this.user.save(data)
return result
}
async 查全部(){
const data = await this.user.find({
where:{
name:Like('%')
}
})
return data;
}
async 查一条(){
/*查询 user 表中 id 字段等于 1 的数据*/
const data = await this.user.finOne({where:{id:1}})
}
async 条件查(){
const data = await this.user.find({
where:{
name:Like(`%${name}%`)
}
})
return data
}
async 改(){
const result = await this.user.update(1, {name:'赵四',doc:"象牙山二付"});
return {
code:101,
data:result,
message:"修改成功"
}
}
async 删除(){
const result = await this.user.delete(id);
console.log(result)
return {
code:101,
data:result,
message:"删除成功"
}
}
}
分页查询
@Injectable()
export class UserService{
constructor(@InjectRepository(User) private readonly user:Repository<User>){}
async findAll(query:{page:number,pageSize:number}) {
console.log((query.page-1)*query.pageSize)
const data = await this.user.find({
where:{
name:Like('%')
},
/**根据id字段进行排序 DESC倒序 ASC正序*/
order:{
id:"DESC"
}
/**分页配置 skip 偏移量;take 要显示的个数*/
skip:(query.page-1)*query.pageSize,
take:query.pageSize
})
/**获取总数 */
const total = await this.user.count({
where:{
name:Like("%")
}
})
return {
code:101,
data:{data:data,total:total},
message:"请求成功"
};
}
}
多表关联及联查
示例 user表 和 tags表 相互关联
user.entity.ts
import { Tags } from "./tags.entity";
@Entity()
export class User {
...
/*和 tags 表建立一对多关系,第一个参数表示关联的实体类,第二个参数指定关联实体中的反向关系字段*/
@OneToMany(()=>Tags,(tags)=>tags.user )
tags:Tags[]
}
rags.entity.ts
import { Column, CreateDateColumn, Entity, Generated, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { User } from "./user.entity";
@Entity()
export class Tags{
...
/*表示和User实体之前存在多对一的关系*/
@ManyToOne(()=>User)
user:User
}
多表联查
const data = await this.user.find({
relatons:['tags'],
...
})
事务
定义: 作为一个单独逻辑工作单元执行的一系列操作,这些操作要么全部执行成功,要么全部失败回滚。事务的目的是确保数据库的一致性和完整性
- 原子性:事务中的操作要么全部成功,要么全部失败回滚,不会出现部分操作失败和成功的情况
- 一致性:事务执行前后,数据库的状态保持一致。如果事务执行成功,数据库的状态会从一个一致的情况转换另一个一致的状态
- 隔离性:事务的执行是相互隔离的,每个事务的操作对其他事务是不可见的,事务之间的操作是相互独立的,不会相互干扰
- 持久性:一旦事务成功提交,其他数据库的修改将永久保存,即将发生系统故障或断电等情况,数据库也能恢复到事务提交的状态
语法
this.moeny.manage.tarnsaction(async manage=>{
manage.save(moeny,{id:1,money:100})
})
配置环境变量
安装
yarn add @nestjs/config --save
在根目录创建 .env
文件,设置环境变量
DB = Mysql
DB_HOOST = localhost
DB_PROT = 3306
DB_NAME = test
DB_USER = root
在 app.module.ts
文件.全局导入
import {ConfigModule} from "@nestjs/config";
@Module({
imports: [
ConfigModule.forRoot({
isGlobal:true
}),
...
],
controllers: [],
providers: [],
})
在其他模块中调用环境变量
import {ConfigService} from "@nestjs/config"
export class TsetCOntroller{
@Get()
getUSer(){
console.log(this.configService.get("DB"));
return ...
}
}
Log日志操作
设置日志级别
在 main.ts
中配置
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule,{
/**关闭nest日志 */
logger:false
/**选择日志级别*/
logger:["log","error","warn"]
});
await app.listen(3000);
}
bootstrap();
在控制器中打印log
@Controller()
export class UserController{
private logger = new Logger(UserController.name)
constructor(){}
@Get()
getUser():any{
this.logger.log("请求成功");
}
}
使用第三方日志管理 pino
安装
yarn add pino-http pino-pretty pino-roll
在全局配置日志系统 app.module.ts
@Module({
imports: [
...
LoggerModule.forRoot({
pinoHttp:{
transport:{
targets:[
{
level:"info",
target:"pino-pretty",
options:{
colorize:true,
},
},
{
level:"info",
target:"pino-roll",
options:{
file:join("logs","log.txt"),
frequency:"daily",
size:"10m",
mkdir:true
}
}
]
}
}
})
...
],
controllers: [],
providers: [],
})
export class AppModule {}
使用第三方日志管理 winston
安装
yarn add --save nest-winston winston winston-daily-rotate-file
配置main.ts
async function bootstrap() {
/*配置winston相关参数*/
const instance = createLogger({
transports:[
new winston.transports.Console({
format:winston.format.combine(
winston.format.timestamp(),
winston.format.simple()
)
}),
new winston.transports.DailyRotateFile({
dirname:"logs",
level:"warn",
filename:"application-%DATE%.log",
datePattern:"YYYY-MM-DD-HH",
zippedArchive:true,
maxSize:"20m",
maxFiles:"14d",
format:winston.format.combine(
winston.format.timestamp(),
winston.format.simple()
)
})
]
})
const app = await NestFactory.create(
AppModule,
{
logger:WinstonModule.createLogger({instance}) /*全局使用*/
}
);
await app.listen(3000);
}
nest登录鉴权(JWT)
安装
yarn add @nestjs/jwt @types/passport-jwt @passport-jwt
在 app.module.ts
中配置
import { JwtModule } from '@nestjs/jwt';
@Module({
imports:[
...
JwtModule.register({
secret:"secret", /*秘钥*/
signOptions:{expiresIn:"1d"} /*过期时间*/
})
]
})
在 controller
层中创建 jwt
import { BadRequestException, Body, Controller, Get, Post, Req, Res, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import {Response,Request} from "express"
@Controller()
export class userController{
constructor(
private jwtService:JwtService
) {}
@Post("login")
async login(@Body() Body){
/*创建jwt*/
const jwt = await this.jwtService.signAsync({id:Body.id})
return jwt
}
@Get()
async user(@Req() require:Request){
/*解析jwt*/
const data = await this.jwtService.verifyAsync(jwt)
return data
}
}