Prisma不能優雅的支持DTO,試試Vona ORM吧
在Nodejs生態中,Prisma是一個非常流行的ORM庫,支持Typescript,提供了非常友好的類型推斷能力。但是,Prisma卻不能優雅的支持DTO。在與其他后端框架整合時,DTO是進行參數驗證、生成Swagger元數據的關鍵節點。如果不能像推斷類型一樣自動推斷出DTO,那么,我們就仍然需要手工創建DTO。隨著業務的增長,復雜的表間關系會讓手工補充DTO的工作日益繁重。
而Vona ORM就提供了非常便利的工具,使我們可以非常直觀的動態推斷出DTO,就像推斷類型一樣,從而解放我們的雙手,顯著提升生產力。甚至可以說,能夠自動推斷DTO,為Nodejs后端框架打開了一扇窗。
Vona本身就是一款更直觀的Nodejs框架,如果大家第一次接觸,可以先參考這篇文章:你認為Vonajs提供的這些特性會比Nestjs更好用嗎?
限于篇幅,這里不展開講解Vona ORM所有的知識點,而是以目錄樹為例,演示如何查詢一棵目錄樹,以及如何動態生成DTO,并最終生成Swagger元數據。Vona框架作者正在直播撰寫Vona文檔。圍觀官方文檔的實時編寫過程,有利于加深對框架設計的理解,探索不一樣的架構設計路徑。有興趣的歡迎移步:B站濮水代碼直播間
1. 創建Entity
在VSCode中,可以通過右鍵菜單Vona Create/Entity創建Entity的代碼骨架:
@Entity('demoStudentCategory')
export class EntityCategory extends EntityBase {
@Api.field()
name: string;
@Api.field(v.optional())
categoryIdParent?: TableIdentity;
}
- 行2: 繼承自EntityBase,就會自動提供5個基礎字段:id、createdAt、updatedAt、deleted、iid
- iid:是實例Id,通過多實例的機制支持多租戶系統的開發
- 行4、7: 定義兩個業務字段:name、categoryIdParent
- @Api.field:通過此裝飾器定義的信息,可同時應用于參數驗證和Swagger元數據
- v.optional:聲明為可選字段
- 更多信息,參見:Vona Entity
2. 創建Model
在VSCode中,可以通過右鍵菜單Vona Create/Model創建Model的代碼骨架:
import { EntityCategory } from '../entity/category.ts';
@Model({ entity: EntityCategory })
export class ModelCategory extends BeanModelBase<EntityCategory> {}
- 行3: entity:指定Model所對應的Entity
- 行4: 繼承自BeanModelBase,從而擁有大量操作數據庫的方法,如:CRUD、聚合、分組,等等
3. 創建樹形結構
如果要創建一棵目錄樹,本質就是建立Model引用自身的遞歸結構。Vona ORM同樣支持4種關系:1對1、1對多、多對1,多對多。那么,在這里,我們就需要采用1對多來創建目錄的自身引用關系。
import { EntityCategory } from '../entity/category.ts';
@Model({
entity: EntityCategory,
+ relations: {
+ children: $relation.hasMany(() => ModelCategory, 'categoryIdParent', {
+ autoload: true,
+ columns: ['id', 'name'],
+ }),
+ },
})
export class ModelCategory extends BeanModelBase<EntityCategory> {}
- 行5: relations:可以定義多個關系
- 行6: children:定義一個1對多的關系
- $relation.hasMany:
- 參數1: 引用自身ModelCategory
- 參數2: 設置關聯外鍵categoryIdParent
- 參數3: 關聯選項
- autoload:是否自動加載關聯數據
- columns:控制關聯數據的字段列表
4. 創建Controller
在VSCode中,可以通過右鍵菜單Vona Create/Controller創建Controller的代碼骨架:
@Controller()
export class ControllerCategory extends BeanBase {}
接下來我們創建一個Api,用于獲取目錄樹:
export class ControllerCategory extends BeanBase {
@Web.get('getCategoryTree')
async getCategoryTree() {
}
}
- 行2: 通過@Web.get定義一個api,path為getCategoryTree
5. 查詢目錄樹
一般而言,我們還需要創建一個Service,從而實現以下調用鏈:Controller->Service->Model->操作數據庫。為了簡化起見,在這里,我們直接在Controller中調用Model方法:
export class ControllerCategory extends BeanBase {
@Web.get('getCategoryTree')
async getCategoryTree() {
const tree = await this.scope.model.category.select({
columns: ['id', 'name'],
});
return tree;
}
}
- 行4: 通過
this.scope取得Category Model,然后調用select方法- columns:指定要查詢的字段列表
由于前面我們設置children關系為autoload: true,因此,查詢結果tree就是一棵完整的目錄樹。下面我們看一下tree的類型推斷效果:


6. 自動推斷DTO
現在我們自動推斷DTO,并且設為API的返回數據的類型:
export class ControllerCategory extends BeanBase {
@Web.get('getCategoryTree')
+ @Api.body(v.array(v.object($Dto.get(() => ModelCategory, { columns: ['id', 'name'] }))))
async getCategoryTree() {
const tree = await this.scope.model.category.select({
columns: ['id', 'name'],
});
return tree;
}
}
- 行3: 通過@Api.body定義API返回數據的類型:
- v.array:定義數組類型
- v.object:定義對象類型
- $Dto.get:動態推斷DTO
- 參數1:指定目標Model
- 參數2:指定選項
- columns:指定要提取的字段列表
同樣,由于前面我們設置children關系為autoload: true,因此,$Dto.get生成的DTO就是一棵完整的目錄樹。下面我們看一下API的Swagger效果:



從示意圖中,我們可以清晰的看到,這棵樹引用的children類型是名稱為demo-student.entity.category_2c7d642ee581efa300341e343180fbb0ecdc785d的動態Entity的數組,從而形成一種遞歸的引用關系。
7. 封裝DTO
雖然我們已經實現了預期的目標,但是Vona ORM提供的能力還沒有結束。我們可以創建一個新的DTO,將前面的代碼$Dto.get(() => ModelCategory, { columns: ['id', 'name'] })封裝起來,從而用于其他地方:
在VSCode中,可以通過右鍵菜單Vona Create/Dto創建DTO的代碼骨架:
@Dto()
export class DtoCategoryTree {}
然后我們通過繼承機制來封裝DTO:
@Dto()
export class DtoCategoryTree
+ extends $Dto.get(() => ModelCategory, { columns: ['id', 'name'] }) {}
現在,我們再使用新創建的DTO來改造前面的API代碼:
export class ControllerCategory extends BeanBase {
@Web.get('getCategoryTree')
+ @Api.body(v.array(v.object(DtoCategoryTree)))
+ async getCategoryTree(): Promise<DtoCategoryTree[]>{
const tree = await this.scope.model.category.select({
columns: ['id', 'name'],
});
return tree;
}
}
- 行3: 直接傳入
DtoCategoryTree - 行4: 返回類型為
Promise<DtoCategoryTree[]>
結語
Vonajs已開源:https://github.com/vonajs/vona。
Vonajs作者正在B站直播撰寫技術文檔,工作日每晚8:30,歡迎圍觀:濮水代碼直播間

Prisma不能優雅的支持DTO。而Vona ORM就提供了非常便利的工具,使我們可以非常直觀的動態推斷出DTO,就像推斷類型一樣,從而解放我們的雙手,顯著提升生產力。甚至可以說,能夠自動推斷DTO,為Nodejs后端框架打開了一扇窗。
浙公網安備 33010602011771號