前言
Node作为一门后端语言,当然也能够连贯数据库,为前端提供CURD接口
咱们以mysql
为例,自行装置mysql
TypeORM
TypeORM 是一个ORM框架,它能够运行在 NodeJS、Browser、Cordova、PhoneGap、Ionic、React Native、Expo 和 Electron 平台上,能够与 TypeScript 和 JavaScript一起应用。 它的指标是始终反对最新的 JavaScript 个性并提供额定的个性以帮忙你开发任何应用数据库的(不论是只有几张表的小型利用还是领有多数据库的大型企业应用)应用程序。
TypeORM
作为TypeScript
中最成熟的对象关系映射器,能够很好的与Nest
框架集成应用。
装置依赖
npm install --save @nestjs/typeorm typeorm mysql2
新建数据库
CREATE DATABASE nanjiu DEFAULT CHARACTER SET = 'utf8mb4';
新建一个nanjiu
数据库
连贯数据库
数据库建好之后,咱们就能够应用typeorm
来连贯数据库并建设映射关系了
// dbConfig.ts// 数据库配置export function dbConfig() { return { type: 'mysql', // 数据库类型 host: '127.0.0.1', // 数据库地址 port: 3306, // 端口 username: 'root', // 用户名 password: '123456', // 明码 database: 'nanjiu', // 数据库名 entities: [__dirname + '/../**/*.entity{.ts,.js}'], // 实体类 synchronize: true, // 主动创立表 autoLoadEntities: true, // 主动加载实体类 } as DbConfig}
须要在app.module.ts
中进行注册
@Module({ imports: [ NanjiuModule, UserModule, InfoModule, TypeOrmModule.forRoot(dbConfig() as any) ], controllers: [AppController], providers: [AppService],})
定义实体
实体是一个映射到数据库表的类,应用@Entity
装璜器来定义
// user.entry.tsimport { Column, Entity, PrimaryGeneratedColumn } from "typeorm";@Entity('user') // 表名export class User { @PrimaryGeneratedColumn() // 自增主键 id: number; @Column() // 字段 name: string;}
根本实体由列和关系组成,每个实体必须有一个主列。
每个实体都必须在连贯配置中注册:
entities: [__dirname + '/../**/*.entity{.ts,.js}'], // 实体类
关联实体
实体定义后须要在module
中导入并关联
@Module({ imports: [TypeOrmModule.forFeature([User])], controllers: [UserController], providers: [UserService]})
当你做完这一步之后你会发现数据库里曾经依据你刚刚定义的实体建好了表
这是因为刚刚数据库配置那里开启了synchronize: true
主动创立表
CURD接口
数据库筹备筹备工作实现后,咱们就能够来写接口了
在controller
控制器中定义接口path
// user.controller.tsimport { CreateUserDto } from './dto/create-user.dto';export class UserController { constructor( private readonly userService: UserService, ) {} @Post('addUser') create(@Body() createUserDto: CreateUserDto) { // 增加用户 return this.userService.add(createUserDto); }}
新建DTO数据验证器
import { Injectable } from "@nestjs/common";import { IsNotEmpty, IsString } from "class-validator"; // 引入验证器@Injectable() export class CreateUserDto { @IsString({ message: '用户名必须是字符串'}) // 验证是否是字符串 @IsNotEmpty({ message: '用户名不能为空'}) // 验证是否为空 name: string; // 用户名}
dto
个别用来做参数验证
注册全局DTO验证管道
// main.tsimport { ValidationPipe } from '@nestjs/common';app.useGlobalPipes(new ValidationPipe()) // 全局验证管道
service逻辑解决,入库操作
// user.service.tsimport { Injectable } from '@nestjs/common';import { CreateUserDto } from './dto/create-user.dto';import { User } from './entities/user.entity';import { InjectRepository } from '@nestjs/typeorm';import { Repository } from 'typeorm';@Injectable()export class UserService { constructor( // 应用 @InjectRepository(User) 注入实数据库实体 @InjectRepository(User) private readonly userRepository: Repository<User> ) {} async add(createUserDto: CreateUserDto) { // 增加用户,更多操作参考 TypeORM 文档 const res = await this.userRepository.save(createUserDto); return res }}
调用接口
查看数据库
调用完接口,此时数据库中会新增一条数据
响应后果解决
从下面的响应后果来看并不标准,只是简略的返回了数据库查问后果,并且当零碎产生异样谬误时,如果咱们没有手动解决异样,所有的异样都会进入到nest
内置的异样解决层,它返回的信息格式如下:
{ "statusCode": 500, "message": "Internal server error"}
比方咱们往user
库中插入雷同的name
,但name
设置了唯一性,所以这时会抛出谬误,如果咱们不解决返回给前端就是下面那种信息,这样前端同学看到就会很蒙,基本不晓得为啥报错
所以咱们要做的就是将响应格式化解决
在nest中,个别是在service中解决异样,如果有异样,间接抛出谬误,由过滤器捕捉,对立格局返回,如果胜利,service把后果返回,controller间接return后果即可,由拦截器捕捉,对立格局返回
失败:过滤器对立解决
胜利:拦截器对立解决
异样拦截器
为了更加优雅地解决异样,咱们能够创立一个异样过滤器,它次要用来捕捉作为HttpException
类实例的异样。
异样抛出封装:
// httpStatus.service.tsimport { Injectable, HttpException, HttpStatus, NestInterceptor } from '@nestjs/common'@Injectable()export class HttpStatusError { static fail(error, status = HttpStatus.BAD_REQUEST) { throw new HttpException({statusCode: status, message: '申请失败', error}, status) }}
抛出异样:
// group.service.ts// ...import { HttpStatusError } from '../utils/httpStatus.service'@Injectable()export class GroupService { constructor( @InjectRepository(Group) private groupRepository: Repository<Group>, @InjectRepository(Template) private templateRepository: Repository<Template>, ) {} // todo: 增加分组 async create(createGroupDto: CreateGroupDto) { const data = this.groupRepository.create(createGroupDto); const group = await this.groupRepository.findOne({ where: { name: createGroupDto.name } }); if (group) { return HttpStatusError.fail('该分组已存在'); } try { const res = await this.groupRepository.save(data, { reload: true }); return res; } catch (error) { return HttpStatusError.fail(error); } }}
异样拦截器封装:
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, } from '@nestjs/common'; @Catch() export class HttpExceptionFilter implements ExceptionFilter { catch(exception: HttpException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); const status = exception.getStatus(); const exceptionRes: any = exception.getResponse(); const { error, message } = exceptionRes; const msgLog = { status, message, error, path: request.url, timestamp: new Date().toLocaleString(), }; response.status(status).json(msgLog); } }
应用:
app.useGlobalFilters(new HttpExceptionFilter()); // 全局异样过滤器
申请:
这样报错信息就可能高深莫测,简略实用的话能够间接抛出异样就能够,而后在抛出异样的中央给出详细信息。
全局响应拦截器
那胜利的响应应该如何优雅地解决呢?
Interceptor拦截器
这里咱们能够应用Interceptor
拦截器,给胜利响应按固定格局返回
import { Injectable, HttpException, HttpStatus, NestInterceptor, ExecutionContext,CallHandler } from '@nestjs/common'import { Observable } from 'rxjs'import { map } from 'rxjs/operators'@Injectable()export class HttpStatusSuccess implements NestInterceptor{ intercept(context: ExecutionContext, next: CallHandler) :Observable<any> { return next.handle().pipe(map(data => { return { statusCode: HttpStatus.OK, message: '申请胜利', data } })) }}
应用:
app.useGlobalInterceptors(new HttpStatusSuccess()); // 全局拦截器申请胜利
申请: