Nest-js 實踐總結
Nest.js 是一個現代的企業級 Node.js Web 框架,最近在使用 Nest.js 實踐一些項目的總結了一些使用心得,也從中學到了很多東西,在這裏總結下來和大家分享。
- API 設置全局前綴
爲 API 設置一個全局前綴可以區分接口版本,如通常會用 /api/v1
作爲的 API 端點的前綴。爲什麼我們需要前綴?好的 API 在設計時要考慮到向後的兼容性。當增強或增加一個 API 時,我們應該確保已經線上使用到該 API 的業務不受影響。簡而言之,API 前綴是爲了向後兼容。
- 模塊劃分
Nest.js 是以模塊化結構爲基礎的,服務端應用應該按功能職責被劃分爲幾個部分,通常情況下,將你的目錄結構應該按模塊劃分而不是按類型分成文件夾。以下是按類型劃分文件夾(不推薦):
以下是按模塊劃分文件夾(推薦):
對於 Nest.js,模塊是一個包含 .module.ts
文件的文件夾,其中包含一個 @Module({})
裝飾器。但並非每個文件夾都需要有一個 .module.ts
文件。例如,你可以創建一個文件夾名爲 utils
來存儲你的工具函數或 JSON 文件。通過將文件組織到模塊文件夾中,會變得清晰,並且可以避免很多錯誤。此外,如果你不遵守此原則,Nest.js 可能會在構建過程中崩潰。
- 使用 DTOs
DTO = 數據傳輸對象。Dtos 就像接口,目標是傳輸數據並驗證它,主要用於路由器 / 控制器。你可以通過使用它們來簡化 API 主體和查詢驗證邏輯。例如,下面的 AuthDto 自動將用戶電子郵件和密碼映射到對象 DTO 以強制驗證。
上面的例子是期望密碼超過 5 個字符,你可以將 dtos 與 class-validator 包配對以自動拋出錯誤。
- 應該使用 Data Mapper/Repository 模式,而不是 Active Record
如果你正在使用 PostgreSQL 或 MySQL 等關係數據庫,那麼請使用 TypeOrm,它是 Typescript 最強大的 ORM 之一。TypeOrm 可以使用兩種模式,一種是由 ruby on rails 推廣的活動記錄模式,另一種是使用存儲庫的數據映射器模式。
使用 Active Record 方法,可以在模型本身內定義所有查詢方法,並使用模型方法保存、刪除和加載對象。下面是使用 Active Record 模式的樣子:
const user = new UserEntity();
user.name = "Vladimir";
user.job = "programmer";
await user.save();
使用 Data Mapper 方法,你可以在稱爲 “存儲庫” 的單獨類中定義所有查詢方法,並使用存儲庫保存、刪除和加載對象:
const user = this.userRepository.create();
user.name = "Vladimir";
user.job = "programmer";
await this.userRepository.save(user);
雖然活動記錄乍一看似乎更好,但它違背了 Nest.js 提供的模塊化,因爲活動記錄與全局實體一起工作,而數據映射器需要在使用它們之前將實體注入每個模塊。數據映射器可能看起來有點冗長,但它是中 / 大型項目的更好解決方案。它也非常適合測試,因爲它適用於依賴注入!
- 應該使用相對路徑,而不是絕對路徑
你可以使用絕對路徑或相對路徑導入 es6 模塊。但在 Nest.js 在開發中使用絕對路徑,再構建應用時它會崩潰。
// relative imports
import { SecurityService } from '../security/security.service';
import { CommentService } from '../comment/comment.service';
// absolute imports
import { SecurityService } from 'src/security/security.service';
import { CommentService } from 'src/comment/comment.service';
- 使用 Exclude 來隱藏不必要的數據
使用過濾器從數據庫中獲取的數據是很常見的。過濾器的整個目標是刪除或格式化來自數據庫的數據。這會導致很多垃圾邏輯,使代碼變得更冗餘。如果是需要隱藏某些字段,可以使用 @Exclude () 裝飾器。
import { Exclude } from 'class-transformer';
export class UserEntity {
id: number;
firstName: string;
lastName: string;
@Exclude()
password: string;
constructor(partial: Partial<UserEntity>) {
Object.assign(this, partial);
}
}
- 使用實體的 getter 方法
一些通用的邏輯可以作爲屬性直接添加到你的實體邏輯裏。最常見的用例與密碼散列和獲取全名有關,這時可以使用 getter 方法,但是要注意不要過度使用,避免給實體承擔大量的業務邏輯。
import { Exclude } from 'class-transformer';
export class UserEntity {
id: number;
firstName: string;
lastName: string;
get fullName() {
return this.firstName + " " + this.lastName;
}
}8. 使用集中命名導出
你可以從同一個文件夾中導入所有類,而不是從不同的文件中導入你的類。如有以下目錄:
// index.ts
export * from './createPost.dto';
export * from './editPost.dto';
export * from './editPostCategory.dto';
export * from './editPostStatus.dto';
// 推薦
import { CreatePostDto, EditPostDto } from './dto';
// 不推薦
import { CreatePostDto } from './dto/createPost.dto';
import { EditPostDto } from './dto/editPost.dto';
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/7nfdzLhWY6kxn_IEA_5o-A