一早起來,國外同事就丟給我 Orval 網站,問我:「用過沒?」
聽都沒聽過啊 T_T
Orval(官方網站為 Orval)是一個開源的前端自動化工具,專門用來把 OpenAPI 或 Swagger 規格書(JSON 或 YAML 格式)自動轉換成具有強型別保護(Type-safe)的 TypeScript API 請求客戶端、Mock 模擬資料和驗證器。
支援 React / Vue / Angular 現代前端框架。
而國外後端同事提供的正是 OpenAPI 規格書,現在就一起來實作看看吧!
因為需求會一直變動,不確定未來哪支 API 會被共用?哪支取消共用?乾脆再開一個 Library 來存放所有的 API,有需要時再 import 即可。
$ ng g library shared-core
在 ./src/lib 底下新增 api 資料夾來存放自動生成的檔案。參考檔案目錄結構如下:
workspace/
├── projects/
│ ├── shared-core/ <-- 新增的核心庫
│ │ └── src/
│ │ ├── lib/
│ │ │ ├── api/ <-- Orval 自動生成的 Type 放這裡
│ │ │ └── auth/ <-- auth.config 放這裡
│ │ └── public-api.ts <-- 統一對外導出 API 功能
│ │
│ ├── shared-todo/ <-- 共用業務庫
│ │ └── ... 這裡可以 import @shared-core
│ │
│ ├── pc/ <-- 專案 A(import @shared-core 與 @shared-todo)
│ └── app/ <-- 專案 B(import @shared-core 與 @shared-todo)
│
├── orval.config.ts <-- 修改輸出路徑至 shared-core
└── package.json
設定路徑別名(Paths Mapping):
到根目錄的 tsconfig.json 新增以下設定,之後 import 便可以使用 @shared-core,而不必多階層的 ../../../...... 來尋找檔案位置。
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
// 設定 shared-core 的引用別名
"@shared-core": ["projects/shared-core/src/public-api.ts"],
}
}
}
PS: 修改完
tsconfig.json必須重新啟動伺服器,Angular 編譯器才有辦法至別新的別名喔!
$ npm install orval -D
在根目錄新增檔案 orval.config.ts:
import { defineConfig } from 'orval';
export default defineConfig({
ctbcApi: {
input: {
target: '後端 url',
},
output: {
mode: 'tags-split', // 依據 Swagger 的 Tags 分門別類產生多個 Service 檔案(若想集中在一檔可改為 'single')
target: './projects/shared-core/src/lib/api/generated/ctbc-api.ts', // 輸出的主檔案路徑
schemas: './projects/shared-core/src/lib/api/generated/type', // 資料模型(Interfaces)存放目錄
client: 'angular', // 指定生成 Angular 專用的 HttpClient 服務
},
},
});
下載 OpenAPI 文件 JSON 檔或直接使用 Server url,儲存在 workspace,target 補上:
ctbcApi: {
input: {
target: './openapi.json',
}
}
在 projects/shared-core/src/public-api.ts 輸出檔案:
// 導出 Orval 生成的 API
export * from './lib/api/generated/ctbc-api';
export * from './lib/api/generated/model';
在 Workspace 根目錄的 package.json 中的 scripts 區塊加入指令:
"scripts": {
"generate:api": "orval"
}
在終端機執行:
$ npm run generate:api


能看到不只是生成 TypeScript 相關檔案,連 HttpClient 都一併完成了!
直接 import Service 就行了!
import { Component, inject } from '@angular/core';
import { ApiService } from '@shared-core';
@Component({
selector: 'app-login',
imports: [LoginForm, NgOptimizedImage],
templateUrl: './login.html',
styleUrl: './login.scss',
})
export default class Login {
private apiService = inject(ApiService);
fetchCaptcha() {
this.apiService.getCaptchaImage().subscribe({
next: (response) => {
console.log('captcha:', response);
},
error: (err) => {
console.error('error:', err);
},
});
}
}
第一次使用會有些難懂,後面上手就很方便囉!
大家也來試試看吧~。
資料來源: