前言
进行spring
开发时,基于注解的AOP
与DI
的编程风格以及ORM
框架的良好支持使得编程层次分明,十分顺滑,但Node
中,常规的使用koa
或express
开发时总要一堆路由函数,且控制层与业务层无法分开,体验不是那么好,而nest
提供了基于装饰器的依赖注入以及面向切面的编程风格,typeorm
实现了node
上的orm
框架,并且支持Mysql
、Postgres
、SQL Server
等等众多数据库,两者结合,可以实现基于装饰器的类似spring
的开发体验,并且nest
提供了对typeorm
的内置支持。
创建Nest
工程
当然,第一步首先是要创建一个nest
工程,nest
和typeorem
主要是基于ts
的,因此首先需安装ts
模块:
yarn global add typescript
然后安装nest
脚手架:
yarn global add @nestjs/cli
利用脚手架创建nest
工程:
nest new NestWithOrm
开始会让选择使用npm
还是yarn
,根据个人喜好选择即可,完成后,会生成一个nest-with-orm
工程(大写自动转成了中划线),将其重命名成NestWithOrm
,然后进入NestWithOrm
文件夹,执行运行命令即可:
yarn run start
打开浏览器,访问3000
端口,即可看到HelloWord
输出。观察工程结构,所有有效代码均放在了src
目录中,其中main.ts
是入口文件,我们先删除除app.service.ts
以及app.controller.ts
文件,我们要自己组织目录结构并编写控制器等。
组织Nest
结构
一般mvc
式风格分service
层、entity
层、controller
层等,我们也以类似的结构组织工程,在src
下新建entity
、controller
、service
、provider
几个文件夹,src
的目录结构将如下所示:
src
|----entity # 放置实体类
|----controller # 放置控制层代码
|----service # 放置服务层代码
|----module # 放置模块代码
|----app.module.ts # 提供可注入对象以及控制器注册
`----main.ts # 入口函数
集成TypeOrm
安装依赖
# nest模块支持
yarn add --save @nestjs/typeorm
# typeorm核心模块
yarn add typeorm --save
# 为typeorm的装饰器提供元数据添加支持
yarn add reflect-metadata --save
# 提供类型定义支持
yarn add install @types/node --save
# 提供数据库驱动,使用哪种数据库,则相应安装哪种数据库的驱动
yarn add mysql2 --save
定义实体类
在src/entity
下新建一个User.ts
,编写一个实体类,如下:
// User.ts
import {Entity, Column, PrimaryGeneratedColumn} from 'typeorm'
@Entity(“user”)
export class User {
@PrimaryGeneratedColumn() id: number //主键,自增
@Column() name: string
@Column() password: string
}
定义服务基类
定义一个服务基类,暴露基本的CURD方法,各实体对应的服务类直接继承即可,这样基本方法每个服务类就不用再写一遍了。
在src/service
目录下新建BaseService.ts
文件,内容如下:
// BaseService.ts
import { Repository, DeleteResult, SaveOptions, FindConditions, RemoveOptions } from "typeorm";
import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity";
import { Injectable } from "@nestjs/common";
/**
* 服务基类,实现一些共有的基本方法,这样就不用每个服务类在写一遍了,直接继承该类即可
*/
@Injectable() export class BaseService<T> {
protected readonly repository: Repository<T>;
constructor(repository: Repository<T>) {
this.repository = repository;
}
async saveOne(entity: T, options?: SaveOptions): Promise<T> {
return this.repository.save(entity, options);
}
async saveMany(entities: T[], options?: SaveOptions): Promise<T[]> {
return this.repository.save(entities, options);
}
async findOne(options?: FindConditions<T>): Promise<T> {
return this.repository.findOne(options);
}
async findMany(options?: FindConditions<T>): Promise<T[]> {
return this.repository.find(options);
}
async findAll(): Promise<T[]> {
return this.repository.find();
}
async removeOne(entity: T, options?: RemoveOptions): Promise<T> {
return this.repository.remove(entity, options);
}
async removeMany(entities: T[], options?: RemoveOptions): Promise<T[]> {
return this.repository.remove(entities, options);
}
async delete(options?: FindConditions<T>): Promise<DeleteResult> {
return this.repository.delete(options);
}
async update(conditions: number | FindConditions<T>, newValue: QueryDeepPartialEntity<T>): Promise<number> {
let updateResult = 1;
await this.repository.update(conditions, newValue).catch(e => updateResult = 0);
return updateResult;
}
}
定义实体对应的服务类
在src/service
目录下新建UserService.ts
文件,内容如下:
// UserService.ts
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { BaseService } from './BaseService';
@Injectable()
export class UserService extends BaseService<User> {
constructor(
@InjectRepository(User) private readonly userRep: Repository<User>
) {
super(userRep);
}
}
定义controller
在src/controller
目录下新建UserController.ts
文件,定义控制层内容,如下:
import { Controller, Get, Param } from '@nestjs/common';
import { UserService } from '../service/UserService';
@Controller("user")
export class AppController {
//通过构造函数注入service
constructor(
private readonly userService: UserService
){}
//定义一个路由
@Get("/")
async getUser(): Promise<User> {
let user = await this.userRep.save({name: 'user1', password: 'pass1'});
return await this.userService.findOne({name: 'user1'});
}
}
定义服务模块
上边定义了实体与实体对应的服务类,要把它们关联起来,需要定义一个模块,在src/module
下新建ServiceModule.ts
,如下:
import {Module} from '@nestjs/common';
import {TypeOrmModule} from '@nestjs/typeorm';
import {User} from 'src/entity/User';
import {UserService} from 'src/service/UserService';
@Module({
imports: [
// 配置数据源
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'test',
autoLoadEntities: true,
synchronize: false
}),
// 引入实体
TypeOrmModule.forFeature([
User,
]),
],
// 服务类作为提供者
providers: [
UserService,
],
// 导出以可被其它模块使用
exports: [
UserService,
]
})
export class ServiceModule {}
将服务模块注册到根模块
最后,将服务模块以及控制器注册到根模块,如下:
import { Module } from '@nestjs/common'
import { UserController } from './controller/UserController'
import { ServiceModule } from './module/ServiceModule';
@Module({
imports: [ServiceModule], // 引入DAO层模块
controllers: [UserController], //注册所有控制器
})
export class AppModule {}
引入reflect-metadata
最后,typeorm
的装饰器需要reflect-metadata
模块的支持,我们需要在入口函数main.ts
中显式引入一下该模块。且要放在引入的第一行,src/main.ts
入口函数将如下所示:
import "reflect-metadata" //显示引入一下
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
await app.listen(3000)
}
bootstrap()
最后
通过上述步骤,nest
与typeorm
已经集成完毕,启动工程:
yarn run start
然后浏览器访问localhost:3000/user
,将会看到以下输出:
{
"id": 1,
"name": "user1",
"password": "pass1"
}