安装
- npm install -g @nestjs/cli
- yarn add @nestjs/cli -g
基础
nest -help 查看快速生成nest代码片段的命令
nest g co快速生成controller,会自动注册到@Modulenest g co --no-spec不生成spec测试文件。 可以在nest-cli.json文件里配置generateOptions:{"spec":false}这样命令无需加上--no-spec也不会生成spec测试文件npm test运行src下所有的spec文件,针对controller生成测试文件
@Body('xx') xx:any body注解接收json参数
/*
1. 在main.ts根module创建数据库的链接
2. 在业务子module创建映射关系
*/
//根module
@Module({
imports: [UserModule,TypeOrmModule.forRoot({
host: 'www.sqlpub.com',
username: 'dora_learn',
password: 'bnAR5BFEC0dAmajG',
database: 'dora_learn',
port: 3306,
type: 'mysql',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}), StudentModule],// 所有子模块都定义在根模块上
controllers: [AppController, StudentController],
providers: [AppService],
})
//业务子module
@Module({
imports: [TypeOrmModule.forFeature([/* entities here */])],//创建映射关系
providers: [StudentService],
controllers:[StudentController]
})
插件:REST Client 定义文件直接发起请求,方便测试接口
1.GraphQL
nest g res生成一个GraphQL类型的文件夹包含dto,entities、resolver、service
resolver中:
@Query(() => [Book], { name: 'findAllBooks' })
findAll() {
return this.booksService.findAll();
}
@Query(() => String, { name: 'book' })
findOne(@Args('id', { type: () => Int }) id: number) {
return this.booksService.findOne(id);
}
❎ query {
book(id:27){
String
}
}
✅ query {
book(id:27)
}
GraphQL 查询不需要指定返回类型为 String,因为这是在 Schema 定义时已经指定的。你只需要调用查询名和传递所需的参数即可。
如果你的查询报错,可能是因为你的查询结构与 Schema 不匹配,或者服务器端的解析器未正确处理该查询。务必确保你的客户端查询与服务端的 GraphQL Schema 中定义的类型和结构一致。
注意点:
- 不能通过直接输入url请求GraphQL
在 Web 开发中,浏览器直接通过地址栏输入 URL 发起的请求默认是 GET 方法。但是,GraphQL API 通常是通过 HTTP POST 方法来接收请求的,因为 GraphQL 查询和变更(mutations)通常会包含复杂的请求体(payload),而 GET 请求通常不会包含请求体。
此外,GraphQL 的设计理念是通过一个单一的端点接收所有的查询和变更请求。这意味着,对于 GraphQL API,你不会有多个 URL 对应不同的操作或资源,而是有一个统一的 URL 端点(如
/graphql),所有的操作都通过这个端点的请求体中传递的查询语句来区分。这就是为什么你不能像调用 REST API 那样直接在浏览器地址栏输入 URL 来调用 GraphQL API。GraphQL 需要接收到完整的查询语句,这在地址栏中无法实现,因为地址栏限制了可以发送的数据类型和长度。
为了在客户端(如前端应用、Postman、curl 等)调用 GraphQL API,你需要构建一个 HTTP POST 请求,并将 GraphQL 查询作为请求体发送到服务器的
/graphql端点。这通常通过编程方式完成,使用如 JavaScript 的fetchAPI、axios库、Apollo Client 或其他 HTTP 客户端库。但是,有些 GraphQL 服务器实现可能支持通过 GET 请求来执行简单的查询。在这种情况下,查询需要被序列化为 URL 的查询参数。这通常用于缓存目的或非常简单的查询,但不是 GraphQL 的主要使用方式。即使是 GET 请求,它们也通常需要通过工具来构建,因为 URL 中需要包含序列化后的查询字符串,这在手动构建时可能会很复杂。
- 前端通过axios等库请求
import axios from 'axios';
const query =
query { findAllBooks { // 在此处填入你想要返回的 Book 对象的字段 id title author // 其他你需要的字段... } };axios.post('http://localhost:3000/graphql', { query: query }) .then(response => { console.log(response.data); }) .catch(error => { console.error('Error fetching data: ', error); });
使用 Apollo Client (专为 GraphQL 设计的库)
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';
const client = new ApolloClient({ uri: 'http://localhost:3000/graphql', cache: new InMemoryCache(), });
const GET_ALL_BOOKS = gql
query { findAllBooks { // 在此处填入你想要返回的 Book 对象的字段 id title author // 其他你需要的字段... } };client.query({ query: GET_ALL_BOOKS }) .then(response => console.log(response.data)) .catch(error => console.error('Error fetching data: ', error));
- 通过postman等工具发起请求
请求 http://localhost:3000/graphql
选择post方法,body->row:
{ "query": "query { findAllBooks { id title author } }" }
注意:Apollo Server 4 开始增加了 CSRF 防护特性,要求客户端在发送 GraphQL 请求时遵守特定的标准: Header头部要设置Content-Type:application/json
2. 异常过滤器
快速创建一个过滤器
nest g f xxx过滤器名称 --no-spec --flat
src下生成xxx过滤器名称.filter.ts 文件
全局使用:
- 在main.ts中使用app.useGlobalFilters进行注册
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './http-exception.filter';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
- 在app.module注入
import { APP_FILTER } from '@nestjs/core';
import { HttpExceptionFilter } from './http-exception.filter';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
单个控制器中使用
@Controller('user')
export class UserController {
constructor(private readonly userService:UserService){}
@Get('/getUser')
@UseFilters(HttpExceptionFilter)
getUser():string{
throw new ForbiddenException('用户不存在')
// return this.userService.getUser()
}
}
自定义异常:处理业务异常
export enum CustomErrorCode {
USER_NOTEXIST = 10002, // 用户不存在
USER_EXIST = 10003, //用户已存在
}
export class CustomException extends HttpException {
private errorMessage: string;
private errorCode: CustomErrorCode;
constructor(
errorMessage: string,
errorCode: CustomErrorCode,
statusCode: HttpStatus = HttpStatus.OK,
) {
super(errorMessage, statusCode);
this.errorMessage = errorMessage;
this.errorCode = errorCode;
}
getErrorCode(): CustomErrorCode {
return this.errorCode;
}
getErrorMessage(): string {
return this.errorMessage;
}
}
这里实现了一个CustomException类继承HttpException类,接收三个参数:错误信息,业务异常码以及http错误码(默认为200,一般业务异常http请求通常是正常的)。同时提供了获取错误码及错误信息的函数。同时导出了一个自定义错误码的枚举
然后我们来到异常过滤器中http-exception.filter.ts做一些逻辑处理
import { ArgumentsHost, Catch, ExceptionFilter,HttpException, NotFoundException } from '@nestjs/common';
import { CustomException } from './custom.exception';
@Catch(HttpException,NotFoundException)
export class HttpExceptionFilter<T> implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp(); // 获取请求上下文
const response = ctx.getResponse(); // 获取响应对象
const request = ctx.getRequest(); // 获取请求对象
const status = exception.getStatus(); // 获取异常的状态码
//判断是否为自定义类
if (exception instanceof CustomException) {
response.status(status).json({
statusCode: exception.getErrorCode(),
message: exception.getErrorMessage(),
timestamp: new Date().toISOString(),
path: request.url,
test:'自定义业务异常:用户异常'
});
return;
}
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message:'自定义异常抛出'
});
}
}
这里通过判断是否是通过自定义异常抛出的错误来返回不同的结果,然后我们在控制层抛出一个自定义错误看一下
import { Controller, ForbiddenException, Get, UseFilters } from '@nestjs/common';
import { UserService } from './user.service';
import { HttpExceptionFilter } from 'src/http-exception.filter';
import { CustomErrorCode, CustomException } from 'src/custom.exception';
// common里存放了各种各样的请求协议
@Controller('user')
export class UserController {
constructor(private readonly userService:UserService){}
@Get('/getUser')
@UseFilters(HttpExceptionFilter)
getUser():string{
// throw new ForbiddenException('用户不存在')
// return this.userService.getUser()
throw new CustomException('用户已存在',CustomErrorCode.USER_EXIST)
}
}