RestQL:現代化的 API 開發方式

參考:https://tech.meituan.com/koa-restql.htmlhtml

在現代的業務系統中,後端開發工做基本上能夠被拆分爲三項:數據庫

  • 接口鑑權。例如判斷是否是當前系統的用戶,以及該用戶是否有權限訪問接口。
  • 與其餘系統的交互。例如調用第三方的服務,或內部搭建的其餘服務。
  • 數據操做。基本上全部須要持久化存儲的系統都會在這項工做上耗費大量時間。

本文將介紹如何利用 RestQL 來很是有效地減小「數據操做」相關的工做量。npm

現狀與挑戰

咱們先來作個假設。後端

  • 假設系統中有 60 張表,每張表對應的接口都要有四種 CRUD 的 API。那麼就須要後端工程師寫60 * 4 = 240個API。
  • 假設上述 60 張表中,40 張表存的是資源類的數據,其他 20 張表爲關係類的數據,也就是說每張表和 20 張表都要進行關聯,每一個關聯也須要四種 CRUD 操做,那麼又要增長40 * 20 * 4 = 3200個API。

因此在上述假設場景中,後端工程師要編寫 3200 + 240 = 3440 個 API。並且這還不是所有,假如後端代碼須要 100% 的測試覆蓋,那麼工程師們就要寫至少 3440 個測試!數組

60 張表 = 3440 個 API + 3440 個單元測試app

衆所周知,數據操做 API 的實現過程基本上是重複的,有的同窗甚至認爲這是低端的,體現不出工程師價值的工做,純粹的「體力活」。可是卻沒有一個能真正解放生產力的方案。koa

解決思路

儘管咱們把數據庫抽象成了「關係型」數據庫,把操做數據的命令抽象成了 SQL ,同時咱們也有了 MySQL 客戶端,甚至是 sequelize 這種很是方便的庫,也有「RESTful」API 命名規則,可是接口的實現歷來都是須要工程師們本身用手敲出來的。工具

若是說我看得比別人遠,那是由於我站在巨人的肩膀上。單元測試

因此咱們在現有的技術基礎上再抽象,把已有的東西從新組合起來,拼裝成一個新的工具,幫助工程師從「體力活」中解脫出來,解放生產力。測試

什麼樣的工具

最開始的時候,咱們最早須要明確的問題就是:「咱們須要什麼樣的工具?」或者說「這種工具要幫咱們解決什麼問題?」。

實際上咱們從剛纔的假設中,已經能夠得出結論:咱們但願有一個工具可讓工程師免於編寫數據操做 API,把數據庫操做直接映射到 HTTP RESTful API 上

調用方式

如何請求

爲了解釋「如何請求」,咱們先從一些公認的規則出發,舉一個例子,而後再從例子中抽象出一些規則。

注意:爲了更便於理解,咱們把全部的命名從客戶端一直穿透到數據庫,因此請不要糾結於咱們在定義一個 API 時名詞單複數的問題。

基本用例

幾乎全部的系統都會有一個用戶表(user)。根據 RESTful 規則的約定,咱們應該把訪問 user 表的 API 路徑定義爲 /user,並把 CRUD 的訪問方法映射到 HTTP 協議中的四種方法:GETPOSTPUTDELETE

好比:

  • GET /user:獲取用戶列表,應該返回一個數組。
  • GET /user/:id:獲取指定的用戶,應該返回一個對象。
  • POST /user:建立一個用戶,應該返回被存儲的對象,狀態碼應該爲 201(Created)。
  • PUT /user:修改一個用戶的信息,應該返回修改後的對象。
  • DELETE /user/:id:刪除一個用戶,狀態碼應該爲 204(No Content)。

若是 user 表有一個關係表 feed,那麼咱們的路徑就會再複雜一點:

  • GET /user/:id/feed 或 GET /feed?user_id=:id:獲取某個用戶的帖子,應該返回一個數組。
  • GET /user/:id/feed/:feed_id 或 GET /feed/:id:獲取指定的帖子,應該返回一個對象。

上述的例子中還會衍生出其餘的數據操做,不單單隻有 GET,這裏不一一列舉了。

抽象出規則

上一節中,列舉了要提供一個表的數據訪問 API,大概要實現哪些路由。從這些枚舉中,能夠找出其中的規律,總結出一套規則。最終咱們在「把能實現的路由,所有實現」的原則基礎上,開發了 RestQL 的 koa 版本。

支持的 HTTP 方法:

HTTP verb CRUD
GET Read
POST Create
PUT Create/Update
DELETE Delete

支持的帶有 body 的 HTTP 方法:

HTTP verb List Single
POST Array/Object ×
PUT Array/Object Object

說明

  • List 路徑爲返回值爲數組的路徑,包括:
    • /resource
    • /resource/:id/association, association 爲 1:n 關係
    • /resource/:id/association, association 爲 n:m 關係
  • Single 路徑爲返回值爲單個對象的路徑,包括:
    • /resource/:id
    • /resource/:id/association, association 爲 1:1 關係
    • /resource/:id/association/:id, association 爲 1:n 關係
    • /resource/:id/association/:id, association 爲 n:m 關係

如何使用

咱們已經開源了 koa-restql,koa 應用開發者能夠經過 npm 安裝它:

npm install koa-restql

而後在 koa 應用的代碼中引用 RestQL:

const koa = require('koa') const RestQL = require('koa-restql') let app = koa() // Build APIs from `sequelize.models` let restql = new RestQL(sequelize.models) app.use(restql.routes()) 

常見問題

修改參數

用戶能夠經過querystring來修改參數。強烈建議使用qs對 querystring 進行解析,例如:

qs.stringify({a: 1, b:2}) // => a=1&b=2 

RestQL 中的querystring僅有 3 條規則:

  • 全部不以_開頭的鍵,都會被放進sequelize#query()where參數中。例如:

    // query { name: "Li Xin" } // option for sequelize { where: { name: "Li Xin" } } 
  • 全部以_開頭的鍵,都會被放進sequelize#query()的參數中,和where保持平級。例如:

    // query { _limit: 10 } // option for sequelize { limit: 10 } 
  • 當須要使用關係時,能夠用關係名稱的字符串代替關係對象傳入。例如須要使用include時:

    // query { _include: ['friends'] } // option for sequelize { include: [ models.user.association.friends ] } 

訪問控制

一般來講,咱們有兩種方法實現訪問控制:

經過中間件

在 koa 應用掛載 RestQL 的 router 以前,能夠實現一個鑑權中間件,控制用戶的訪問權限:

app.use(authorizeMiddleware)
app.use(restql.routes())

Authorize Middleware

經過 restql 參數

在使用sequelize定義關聯時,咱們能夠設定restql參數,實現訪問控制。例如:

  • 禁止經過restql訪問關聯:

    models.user.hasOne(
        models.privacy,
        {
          restql: {
            ignore: true } } ) 
  • 禁止經過restql使用指定的 HTTP 方法訪問關聯

    models.user.hasOne(
        models.privacy,
        {
          restql: {
            ignore: ['get'] } } )
相關文章
相關標籤/搜索