從頭建立一套 web-based 服務架構 (9) 開發 Loopback App
繼昨天介紹了 API Server 的基本架構後,今天要來簡單介紹 Loopback 為了快速開發而提供的諸多方便功能。
Config
Loopback 的概念處處充滿 declarative,也就是以 JSON 檔設定需要啟用的功能和它們的設定;這和自行呼叫程式碼進行設定的 imperative 相比,能夠更簡易、不出錯地進行環境設定,但也較不具彈性。
這些設定檔會放在 src/config 資料夾內。
除了使用 JSON 格式,所有的設定檔均能使用一般的 JavaScript 格式,寫成 CommonJS module 並於 module.exports 輸出和 JSON 檔相同的 JavaScript 物件即可。
這個作法可以用來和環境變數整合,讓有需要的設定值能夠於佈署時期決定。
以下介紹一些常用的設定值:
config.json: Server instance 本身的相關設定restApiRoot: 設定 RESTful API 的 root path,一般而言不會使用 server 本身的根路徑/,而是用一兩層 path 進行包裝,例如/api或/api/v1。host和port用來傳給 Node.js 原生的httpmodule 中的server.listenAPI。remoting.rest.normalizeHttpPath把 URL path 正規化(underscore 變 dash、Camel case 變 Kebab case)remoting.rest.xml: 設定是否要接受appliaction/xml類型的Content-Typeremoting.json: 傳給bodyParser用的參數設定remoting.urlencoded: 傳給bodyParser.urlencoded用的參數設定
datasources.json: Loopback 允許設定複數的 ORM/ODM 綁定,支援範圍包括 MySQL、MongoDB 甚至 in-memory 的 redis 均可。不同的 datasource 類型以connector做區分,並需要安裝對應的loopback-connector-*套件。component-config.json: Loopback 可以額外安裝不同的 plugin(稱為 Loopback component),而 Loopback 會用這個檔案來設定 component,例如 Swagger UI 使用的loopback-component-explorer。model-config.json: 這裡需要設定的是所有在src/common/models定義的 model 資料所對應的 datasource,也可以用public設定是否要為這些 model 開放 RESTful API。
Bootstrapping Scripts
loopback-boot 套件會讀取使用者指定的路徑,將裡面所有的 bootstrapping scripts 依照檔案名稱順序全部執行一次。常用的 bootstrapping script 包括:
- 讀取 roles 設定
- 替這個 API Server 建立一個 default 的 admin 使用者
- 進行 database migration
Model Definition
Loopback 最棒的價值在於直接定義好 model 就能自動生成 RESTful API endpoints 和 database schema。
這些 model definition 會放在 src/common/models,每個 JSON 檔對應一個 model。
Roles
Roles 是用於 Access Control List (ACL),可以非常精細的控制不同的 API 的存取權限。
Remote Methods
除了 Loopback 預設生成的 RESTful API,我們當然能自行宣告客製化的 API 掛載到 model 上,這些客製化的部份稱為 remote method。
宣告 remote method 需要在 src/common/model 宣告 model 的 JSON 檔時,一起宣告一個 .js 的 JavaScript 檔案(例如 Assignment.json 和 Assignment.js)。
Loopback 會在 bootstrapping 時期將 Model 類別作為參數傳入 JS 檔 export 出來的 function,讓開發者在 function 內進行 remote method 的宣告。
通常要宣告一個 remote method 有兩個步驟:
1) 定義 method 本體 的 function。
這個 function 的輸入參數是標準的 Node.js callback style,由前面的輸入參數和最後一個 callback 參數構成,所有的回傳值也要以 callback(err, result) 傳回。
在 function 內部就能透過其他的 Model method 來對 datasource 操作,取得想要的結果後再以 callback 回傳。
Method 有分 static method 和 object method 兩種,顧名思義就是分別定義於 Model class 和 Model instance 上的 method,兩者可分別用 Model.aMethod 和 Model.prototype.aMethod 進行定義。
2) 呼叫 Model.remoteMethod 並傳入 remote method 的設定值。
在設定裡面可以細部定義這個 API 的細節,包括 API 的 verb、URL path、有哪些輸入參數(要傳給 (1) 定義的 function 用的)、輸入參數的來源(query string、URL path argument、request header/body 等)、response body 的結構定義等。
以下用 user model 為例,在 user instance 上宣告一個改變密碼的 remote method:
module.exports = User => {
User.prototype.changePassword = function changePassword(newPassword, callback) {
this.updateAttributeAsync('password', newPassword)
.then(user => callback(null, { message: 'success' }))
.catch(err => callback(err));
};
User.remoteMethod('changePassword', {
isStatic: false,
accepts: [
{ arg: 'newPassword', type: 'string' }
],
returns: {
arg: 'info', type: 'object', root: true
},
http: { path: '/password', verb: 'put' },
description: 'Change password of a user'
});
};
用 loopback-component-passport 設定 local login
Loopback 在 loopback-component-passport 套件實作了基於 access token 的登入流程,提供 user、access token 等 model 後便能直接使用:
import { PassportConfigurator } from 'loopback-component-passport';
app.use(loopback.token({
model: app.models.accessToken
}));
const passportConfigurator = new PassportConfigurator(app);
passportConfigurator.init(true);
passportConfigurator.setupModels({
userModel: app.models.user,
userIdentityModel: app.models.userIdentity,
userCredentialModel: app.models.userCredential
});
passportConfigurator.configureProvider('local', {
authScheme: 'local',
provider: 'local',
module: 'passport-local',
usernameField: 'username',
passwordField: 'password',
authPath: '/auth/local'
});
整合 Swagger UI
Swagger UI 是個功能強大的 API「瀏覽器」。這個介面會將所有 RESTful API 以 Model 為分隔各自列出,並條列每個 API 的細節,最重要的是可以直接在這個 web app 上呼叫這些 API 進行手動測試。
Loopback 使用 loopback-component-explorer 將 Swagger UI 整合到 Loopback 系統內,稱為 Strongloop API Explorer。
在 component-config.json 宣告設定細節:
{
"loopback-component-explorer": {
"mountPath": "/explorer",
"apiInfo": {
"title": "API explorer",
"description": "API explorer"
}
}
}
以下是 Strongloop API Explorer 的畫面:

從 API 測試區域也能看到根據 remote method 的設定而生成的 UI:
