nestjs使用Typeorm實現數據庫CRUD操作

本示例在nestjs腳手架項目基礎上,進行了一些修改,並通過TypeOrm實現了數據庫的增刪讀寫操作。由於Typeorm更適合關係型數據庫,本示例為簡便起見,選擇sqlite3作為試驗數據庫。對於非關係型數據庫如mongodb,則推薦使用mongoose或者typegoose等庫進行數據庫操作。

1.nestjs安裝和快速啟動


nestjs使用Typeorm實現數據庫CRUD操作


安裝nestjs命令行工具


<code>#安裝cli工具
$ npm i-g @nestjs/cli
#新建項目
$ nest new projectName
#如果用yarn安裝的話
$ yarn global add @nestjs/cli/<code>


2.模版項目helloworld說明


不同版本的cli命令行生成的默認項目可能有所不同。這裡將默認的helloworld修改為async異步形式來對nestjs方法進行說明。


2.1 app.controller.ts文件


<code>import { Controller, Get, Inject } from '@nestjs/common';

import { AppService } from './app.service';

/**
* 應用程序控制器,@Controller() 可以指定參數,用於定義類的父路由,如 @Controller("cat"),此時這個類的所有父路由就會成為 /cat

*
* 被 @Controller() 修飾的類,可以通過其構造函數完成依賴注入,但依賴注入的類必須與當前類屬於同一個模塊
*/
@Controller()
export class AppController {

/**
* 構造函數,用於注入這個類的依賴,注入類時,需要使用 @Inject() 修飾符,其參數是被注入的類的類名

* 在注入被 @Injectable() 修飾的類時,可以不使用 @Inject() 修飾參數,此時依賴注器入會使用參數的類型完成注入
*
* Tips: 這裡使用 @Inject(AppService) 是為了規範代碼風格
*/
constructor(
@Inject(AppService) private readonly appService: AppService,
) { }

/**
* @Get() 可以指定參數,用於定義方法路由,如 @Get(":id"),此時這個方法路由就會成為 /:id,即查詢指定ID
*/
@Get()
async root() {
return this.appService.root();
}
}/<code>


2.2 app.service.ts文件


nestjs使用Typeorm實現數據庫CRUD操作


<code>import { Injectable } from '@nestjs/common';

/**
* 被 @Injectable() 修飾的類,可以通過其構造函數完成依賴注入,但依賴注入的類必須與當前類屬於同一個模塊
*/
@Injectable()
export class AppService {
constructor() { } // 構造函數,一般用於處理依賴注入

async root() {
return 'Hello World!';
}
}/<code>


2.3 app.module.ts文件


<code>import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CatModule } from 'cats/cat.module';
import { ErrorsInterceptor } from 'common/errors.interceptor';

import { AppController } from './app.controller';
import { AppService } from './app.service';

/**
* @Module() 定義一個模塊,並管理這個模塊的導入集合、控制器集合、提供者集合、導出集合
*/
@Module({
// TypeOrmModule.forRoot() 默認加載項目根目錄下的 ormconfig.json 配置文件用於配置數據庫連接

// TypeORM 配置文件詳細文檔 https://typeorm.io/#/using-ormconfig
imports: [TypeOrmModule.forRoot(), CatModule], // 導入其他模塊的集合
controllers: [AppController], // 當前模塊的控制器集合
providers: [
{
provide: APP_INTERCEPTOR,
useClass: ErrorsInterceptor
},
AppService
], // 當前模塊的提供者集合
exports: [], // 導出當前模塊的提供者,用於被其他模塊調用
})
export class AppModule { }/<code>


2.4 main.ts入口文件


<code>import { NestFactory } from '@nestjs/core';

import { AppModule } from './app.module';

async function bootstrap() {
const app = await NestFactory.create(AppModule); // 創建應用程序實例,此時所有被 AppModule 導入的其他模塊的所有實例都會被加載
await app.listen(3000); // 使用3000端口監聽應用程序
}

bootstrap(); // 啟動應用程序 -> localhost:3000/<code>


2.5 app.controller.spec.ts測試文件


修改了上述文件之後,命令行默認生成的測試文件也需要修改為異步的方式才能通過測試。


<code>import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';

describe('AppController', () => {
let appController: AppController;

beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();

appController = app.get<appcontroller>(AppController);
});

describe('get', () => {
it('should return "Hello World!"', async() => {
const data=await appController.root();
expect(data).toBe('Hello World!');
});
});
});/<appcontroller>/<code>


3. 通用錯誤處理和接口文件


在項目實踐中,注入錯誤處理、接口等模塊化文件需要重複使用。最好集中在公用模塊中。在項目根目錄下創建common文件夾,並新建錯誤處理和接口模塊。


nestjs使用Typeorm實現數據庫CRUD操作


3.1 common/errors.interceptor.ts錯誤處理文件


<code>import { CallHandler, ExecutionContext, HttpException, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class ErrorsInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable {

// 異常攔截器,攔截每個請求中的異常,目的是將異常碼和異常信息改寫為 { code: xxx, message: xxx } 類型
return next.handle().pipe(catchError((error, caught): any => {
if (error instanceof HttpException) {
return Promise.resolve({
code: error.getStatus(),
message: error.getResponse()
});
}
return Promise.resolve({
code: 500,
message: `出現了意外錯誤:${error.toString()}`
});
}));
}
}
/<code>


3.2 common/result.interface.ts接口文件


<code>// 定義通用的API接口返回數據類型
export interface Result {
code: number;
message: string;
data?: any;
}/<code>


4.創建數據庫增刪讀寫API文件


在根目錄下新建cat文件夾,並依次創建下列文件。


4.1 cat.controller.ts


<code>import { Body, Controller, Delete, Get, Inject, Param, Post, Put } from '@nestjs/common';
import { Result } from 'common/result.interface';

import { Cat } from './cat.entity';
import { CatService } from './cat.service';

@Controller('cat')
export class CatController {
constructor(
@Inject(CatService) private readonly CatService: CatService,
) { }

@Post()
async createCat(@Body() Cat: Cat): Promise<result> {
await this.CatService.createCat(Cat);
return { code: 200, message: '創建成功' };
}

@Delete(':id')
async deleteCat(@Param('id') id: number): Promise<result> {
await this.CatService.deleteCat(id);
return { code: 200, message: '刪除成功' };
}

@Put(':id')
async updateCat(@Param('id') id: number, @Body() Cat: Cat): Promise<result> {
await this.CatService.updateCat(id, Cat);
return { code: 200, message: '更新成功' };
}

@Get(':id')
async findOneCat(@Param('id') id: number): Promise<result> {
const data = await this.CatService.findOneCat(id);
return { code: 200, message: '查詢成功', data };
}
}/<result>/<result>/<result>/<result>/<code>


4.2 cat.service.ts文件


<code>import { HttpException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

import { Cat } from './cat.entity';

@Injectable()
export class CatService {
constructor(
@InjectRepository(Cat) private readonly catRepo: Repository, // 使用泛型注入對應類型的存儲庫實例
) { }

/**
* 創建
*
* @param cat Cat 實體對象
*/
async createCat(cat: Cat): Promise {
/**
* 創建新的實體實例,並將此對象的所有實體屬性複製到新實體中。 請注意,它僅複製實體模型中存在的屬性。
*/
// this.catRepo.create(cat);

// 插入數據時,刪除 id,以避免請求體內傳入 id
delete cat.id;
return this.catRepo.save(cat);

/**
* 將給定實體插入數據庫。與save方法不同,執行原始操作時不包括級聯,關係和其他操作。
* 執行快速有效的INSERT操作。不檢查數據庫中是否存在實體,因此如果插入重複實體,本次操作將失敗。

*/
// await this.catRepo.insert(cat);
}

/**
* 刪除
*
* @param id ID
*/
async deleteCat(id: number): Promise<void> {
await this.findOneById(id);
this.catRepo.delete(id);
}

/**
* 更新
*
* @param id ID
* @param cat Cat 實體對象
*/
async updateCat(id: number, cat: Cat): Promise<void> {
await this.findOneById(id);
// 更新數據時,刪除 id,以避免請求體內傳入 id
delete cat.id;
this.catRepo.update(id, cat);
}

/**
* 根據ID查詢
*
* @param id ID
*/
async findOneCat(id: number): Promise {
return this.findOneById(id);
}

/**
* 根據ID查詢單個信息,如果不存在則拋出404異常
* @param id ID
*/
private async findOneById(id: number): Promise {
const catInfo = await this.catRepo.findOne(id);
if (!catInfo) {
throw new HttpException(`指定 id=${id} 的貓貓不存在`, 404);

}
return catInfo;
}
}
/<void>/<void>
/<code>


4.3 cat.entity.ts數據庫實體文件


<code>import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity('cat')
export class Cat {
/**
* 自增主鍵
*/
@PrimaryGeneratedColumn({
comment: '自增ID'
})
id: number;

/**
* 暱稱
*/
@Column({
comment: '暱稱'
})
nickname: string;

/**
* 品種
*/
@Column({
comment: '品種'
})
species: string;
}/<code>


4.4 cat.module.ts模塊文件


<code>import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

import { CatController } from './cat.controller';
import { Cat } from './cat.entity';
import { CatService } from './cat.service';

@Module({
imports: [TypeOrmModule.forFeature([Cat])],
controllers: [CatController],
providers: [CatService],
})
export class CatModule { }/<code>


5.配置數據庫


typeorm可以和各種數據庫配合使用。為了方便調試,這裡用sqlite3替換掉原案例教程中的Postgresql(否則還要裝一個Postgresql數據庫)。

在項目中安裝sqlite3驅動。


<code>> npm i --save sqlite3/<code>


在根目錄ormconfig.json文件中進行如下配置。注意,原案例中entities的目錄在src/文件夾下面,實際使用時可能會報錯(如果是直接運行TypeScript不會報錯,如果編譯成JavaScript就會報錯,後面一種情況下需要修改目錄到dist或者其他編譯目標文件夾下)。


<code>{
"type": "sqlite",
"database": "./mydb.sql",
"entities": [
"dist/**/**.entity{.ts,.js}"
],
"synchronize": true,
"logging": true
}/<code>


6.項目依賴包


項目中依賴的包文件如下,其中nestjs cli 已經安裝好了一部分,typeorm等需要手動添加。


<code>  "dependencies": {
"@nestjs/common": "^6.7.2",
"@nestjs/core": "^6.7.2",
"@nestjs/platform-express": "^6.7.2",
"@nestjs/typeorm": "^6.2.0",
"mongodb": "^3.4.1",
"pg": "^7.16.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.0",
"rxjs": "^6.5.3",
"sqlite3": "^4.1.1",
"typeorm": "^0.2.22"
},
"devDependencies": {
"@nestjs/cli": "^6.9.0",
"@nestjs/schematics": "^6.7.0",
"@nestjs/testing": "^6.7.1",
"@types/express": "^4.17.1",
"@types/jest": "^24.0.18",
"@types/node": "^12.7.5",

"@types/supertest": "^2.0.8",
"jest": "^24.9.0",
"prettier": "^1.18.2",
"supertest": "^4.0.2",
"ts-jest": "^24.1.0",
"ts-loader": "^6.1.1",
"ts-node": "^8.4.1",
"tsconfig-paths": "^3.9.0",
"tslint": "^5.20.0",
"typescript": "^3.7.4"
}/<code>


7. 其他資源


-typeorm倉庫

-Nestjs中文文檔

-Jest測試框架中文文檔

-github nest學習資源


分享到:


相關文章: