从零开始搭建搭建nestJs
Nest 概念
-
Nest的核心概念是提供一种体系结构,它帮助开发人员实现层的最大分离,并在应用程序中增加抽象。
-
主要有三个核心概念:模块Module, 控制器Controller, 服务与依赖注入 Provider Dependency injection
- 模块Module: 用于将代码拆分为独立的和可重用的模块,例如用户信息模块,然后将该用户模块的控制器和服务集合进来,最后直接将用户模块导入到根Module就可以使用了。
- 控制器Controller: 负责处理客户端传入的请求参数并向客户端返回响应数据。nest.js提供许多http请求的装饰器,如例如@Body(),@Post()等。控制器不需要定义任何诸如从客户端获取数据、验证用户输入等工作,这些都是交给服务Provider处理,通过把任务委托给各种服务,可以让控制器类更加精简、高效。``
- 服务Provider :在这里处理所有请求执行逻辑,在控制器中通过constructor函数以依赖注入的方式实现。
-
其他概念:
- 过滤器Exception filter:负责在整个应用程序中处理所有抛出的异常,同时可以自定义错误状态码和错误消息内容。
- 管道 Pipe:管道可以把你的请求参数根据特定条件验证类型、对象结构或映射数据。管道是一个纯函数,不应该从数据库中选择或调用任何服务操作。
- 守卫 Guard:通常用作权限认证
- 中间件Middleware: 中间件功能可以访问请求和响应对象,在路由处理程序之前调用
客户端请求 —> 中间件 —> 守卫 —> 拦截器之前 —> 管道 —> 控制器处理并响应 —> 拦截器之后 —> 过滤器
一、创建nest
nest -h指令介绍
class (简写: cl) 类
controller (简写: co) 控制器
decorator (简写: d) 装饰器
exception (简写: e) 异常捕获
filter (简写: f) 过滤器
gateway (简写: ga) 网关
guard (简写: gu) 守卫
interceptor (简写: i) 拦截器
middleware (简写: mi) 中间件
module (简写: mo) 模块
pipe (简写: pi) 管道
provider (简写: pr) 供应商
service (简写: s) 服务
nest 利用cli创建,会自动生成并引入模块,
- 安装与创建
npm i -g @nestjs/cli
nest new project-name
npm start
- 创建模块(用于关联路由与服务)
nest generate module news
简写
nest g mo news
news.module.ts
import { Module } from '@nestjs/common';
import { NewsService } from './news.service';
import { NewsController } from './news.controller';
//创建时会自动引入到module中管理,同时会把module导入到app.module中
@Module({
providers: [NewsService],
controllers: [NewsController]
})
export class NewsModule {}
- 创建路由(控制层,可以处理简单的业务和处理请求与响应)
nest generate controller news
简写
nest g co news
news.controller.ts
import { Controller, Get } from '@nestjs/common';
import { NewsService } from './news.service';
@Controller()
export class AppController {
constructor(private readonly appService: NewsService) {} //将news.serbice中注入的属性,在这里注册就可以在this上使用
@Get()
getHello(): string {
return this.NewsService.getHello();
}
}
- 建立服务(处理复杂的业务逻辑)
nest generate service cats
简写
nest g s cats
news.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()//注入到底层,在controller中就可以使用
export class NewsService {
getHello(): string{
return 'Hello World!';
}
}
app.module.ts
这部分很关键,需要将controllers引入,等同于加入路由操作
如果使用了service 也需要在下面引用,
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DbModule } from '@libs/db';
import { NewsController } from './news/news.controller';
import { NewsService } from './news/news.service';
@Module({
imports: [
DbModule
],
controllers: [AppController,NewsController ],
providers: [AppService,NewsService],
})
export class AppModule {}
二、openAPI (swagger编写API文档)
- nest原生支持swagger,可以自动根据装饰器解析对应的文档
npm install --save @nestjs/swagger swagger-ui-express
安装过程完成后,打开main.ts文件并使用SwaggerModule类初始化Swagger:
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const options = new DocumentBuilder()
.setTitle('nest博客api') //标题
.setDescription('我的第一个nest项目') //描述
.setVersion('1.0') //版本
//.addTag('cats') //添加标签
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('api-doc', app, document);//定义挂载到那个路径下
await app.listen(3000);
console.log('http://localhost:3000/api-doc')
}
bootstrap();
-
结合swagger使用 文档
//参数装饰器
//@Body(key?:string),@Query(key?:string),@Param(key?:string)等同于 res.body[key] 指定获取某一值
import { Controller, Get, Post, Body, Query, Param, Put, Delete } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiProperty,ApiQuery,ApiCreatedResponse, ApiParam } from '@nestjs/swagger'; import { query } from 'express'; // 这里不使用interface 原因是 interface编译成js后会被删除,同时用不了装饰器,无法在swagger显示 class CreatePostDto{ //用于对参数的限制 @ApiProperty({description:'帖子标题',example:'111'}) title:string @ApiProperty({description:'帖子内容',example:'222'}) content:string } @Controller('posts') //相当于添加一个路由前缀 @ApiTags('帖子') //添加一个api标签 export class PostsController { @Get() @ApiOperation({summary:'显示博客列表'})//Api注解 // 帖子列表页 @ApiQuery({ name: 'id',example:1,description:'列表id'}) index(@Query()query){ return[ {id:1,title:'博客1'}, {id:1,title:'博客2'}, {id:1,title:'博客3'}, ] } @Post() @ApiOperation({summary:'创建帖子'}) @ApiCreatedResponse({ //编写响应的api注解 status:200, description: 'The record has been successfully created.', type: CreatePostDto, }) create(@Body()createPostDto:CreatePostDto){ return{ data:createPostDto, success:true } } //params 和query获取的都是一个对象,可以在装饰器中指定获取的参数 @Get(':id') @ApiParam({ name: 'id',example:1,description:'帖子id'}) @ApiOperation({summary:'获取帖子详情'}) async detail(@Param('id')id:string){ return { data:data, success:true } } @Put(':id') @ApiOperation({summary:'修改帖子详情'}) updatePostDto(@Param()id,@Body() updatePostDto:CreatePostDto){ return { data:{ id:id.id, data:updatePostDto }, success:true } } @Delete(':id') @ApiOperation({summary:'删除帖子'}) remove(@Param()param){ return{ data:param.id, success:true } } }
-
RESTful接口规范 GET/POST/PUT/DELETE
REST 是Representational State Transfer的缩写,翻译是”表现层状态转化”. 面向资源是REST最明显的特征,对于同一个资源的一组不同的操作。资源是服务器 上一个可命名的抽象概念,资源是以名词为核心来组织的,首先关注的是名词。REST要求,必须通过统一的接口来对资源执行各种操作。对于每个资源只能执行一组有限的操作。 get 获取 post 创建 put 更新 delete 删除 状态码 200(OK) - 表示已在响应中发出 204(无内容) - 资源有空表示 301(Moved Permanently) - 资源的URI已被更新 303(See Other) - 其他(如,负载均衡) 304(not modified)- 资源未更改(缓存) 400 (bad request)- 指代坏请求(如,参数错误) 404 (not found)- 资源不存在 406 (not acceptable)- 服务端不支持所需表示 500 (internal server error)- 通用错误响应 503 (Service Unavailable)- 服务端当前无法处理请求
三、配置静态资源与模板引擎
在根目录新建public文件夹
main.ts
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>( //注入平台
AppModule,
);
app.useStaticAssets(join(__dirname, '..', 'public'));//配置静态资源
// 或者
// app.useStaticAssets('public',{
// prefix:'/static/' //配置虚拟目录 上面一样
// })
app.setBaseViewsDir(join(__dirname, '..', 'views'));//配置模板文件存储文件夹
app.setViewEngine('ejs');//配置模板引擎需要安装不需要引入
await app.listen(3000);
}
bootstrap();
在根目录创建views文件夹,然后在下面创建index.ejs
之后在需要的controller中引入,
import { Controller, Post, Render, Get } from '@nestjs/common';
import { NewsService } from './news.service';
@Controller('news')
export class NewsController {
constructor(private readonly newsSerbice:NewsService){}
@Get()
@Render('index')
getEl(){
return {} //要么没有返回值,要么返回一个json
}
}
四、创建子项目(运用于多个项目共用model)
在一个nest项目创建同级项目中统一管理
cd project-name
nest g app children
//或者
nest g 整体项目名 子项目名
指定子项目中增加模块/控制器
nest g mo/co/s -p 子项目名 模块名
neest g mo -p admin users
这个时候在整体项目下会出现2个子项目这个时候的启动命令变成
//dev时需要加-w监听,子项目名是什么就会监听谁
nest start -w chidren-name
五、创建公用库(存放类似db之类)
//创建一个db公用库
nest g lib db
//默认是@app, 最好改成@libs方便记忆
? What prefix would you like to use for the library (default: @app)? @libs
安装完成后在需要使用的模块的app.module.ts中引入dbmodule
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DbModule } from '@libs/db';
@Module({
imports: [
DbModule
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
在lib文件下的src下的db.module.ts中连接数据库
import { Module } from '@nestjs/common';
import { DbService } from './db.service';
import { TypegooseModule } from "nestjs-typegoose"
@Module({
imports:[
TypegooseModule.forRoot("mongodb://localhost:27017/nest-MDemo",{
useNewUrlParser:true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false
})
],
providers: [DbService],
exports: [DbService],
})
export class DbModule {}
然后在src下创建文件夹models 例如:创建一个user.model.ts
import {prop} from "@typegoose/typegoose"
export class User {
@prop()
username:string
@prop()
password:string
}
注册模块(可以在需要的模块中注册,也可以这样全局注册,就可以任意使用)
import { Module, Global } from '@nestjs/common';
import { DbService } from './db.service';
import { TypegooseModule } from "nestjs-typegoose"
import { User } from './models/user.model';
const models = TypegooseModule.forFeature([User])//注册模块
@Global()//定义为全局
@Module({
imports:[
TypegooseModule.forRoot("mongodb://localhost:27017/nest-MDemo",{
useNewUrlParser:true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: true
}),
models
],
providers: [DbService],
exports: [DbService,models],
})
export class DbModule {}
使用方式和单个项目一样 需要把lib下定义的user.model 导入然后注入