GraphQL —— 標量類型

標量(ScalarTypeDefinition)是 GraphQL 中不可分割的原子數據類型,在服務中充當葉子節點。對於客戶端而言,合法的查詢集(Select Set)必須到達葉子節點,也就是到達標量類型節點。javascript

GraphQL 規範提供了五種標量:java

  1. Int: 32 位有符號整型,超出精度範圍後,引擎會拋出異常
  2. Float: 有符號雙精度浮點數,超出精度範圍後,引擎會拋出異常
  3. String: 字符串,用於表示 UTF-8 字符序列
  4. Boolean: bool 值
  5. ID: 資源惟一標誌符

1. ID 特性

上述五種類型與其餘語言對應的類型定義類似,相信讀者老爺們都已經很是熟悉,無需贅述,惟一值得探討的是 ID 類型。git

  1. 表現上 ID 類型只是一個字符串格式的值,引擎支持字符串解析值,也支持將 Int 解析值轉換爲字符串類型;
  2. 語義上"ID" 類型應該用於惟一標誌一個資源對象,也就是說,使用相同 ID 值,不管查詢多少次,結果都應該是同一對象,這一點有助於實現緩存,是 GraphQL 推薦的緩存方案;
  3. 引擎並不限制解析值的惟一性,查詢結果包含多個 ID 值相同的節點是合法的。

咱們來看一下例子加深印象:github

[
 // 字符串類型
 {id: '1'},
 // int 類型,引擎會將其轉換爲字符串
 {id: 1},
 // float 類型
 // 非法值,引擎不支持float轉換
 // 將拋出 `TypeError` 錯誤
 {id: 1.2},
 // 與上面第一條重複
 // 合法值,引擎並不強制 `ID` 值的惟一性
 {id: '1'}
]

2. 自定義標量類型

除規範定義的標量外,還能夠按需定義業務範疇內的標量。語法很是簡單:緩存

scalar Datetime

注意,這只是語義範疇定義,還須要定義序列化、反序列化函數:函數

new GraphQLScalarType({
  name: "Datetime",
  description: "日期時間標量類型",
  // 序列化函數
  serialize(value) {
    return value.toString();
  },
  // 解析函數
  parseValue(value) {
    if (typeof value === "string") {
      return new Date(value);
    }
    throw new Error("參數類型錯誤");
  },
  // 解析函數
  parseLiteral(ast) {
    if (ast.kind === Kind.STRING) {
      return new Date(ast.value);
    }
    throw new Error("參數類型錯誤");
  }
});

下面咱們一個一個看這些配置:工具

  1. name: 字段名,請保持與 schema 中定的標量類型名稱保持一致
  2. description: 類型描述,在一些診斷工具上仍是頗有用的
  3. serialize: 序列化函數,用於將結果轉換爲適合 http 傳輸的數值類型
  4. parseValue: 解析函數,用於將客戶端經過 variables 參數傳遞的數值爲 Date 類型
  5. parseLiteral: 一樣是解析函數,將客戶端傳遞的 字面量參數 解析爲 Date 類型

配置中的 parseValueparseLiteral 兩個函數功能上類似,都用於解析客戶端參數,分別處理兩種參數傳遞方式:scala

# variables 參數
# 引擎將調用 parseValue 函數
query (before: Datetime){
  users(before: $before) {
    id
    name
  }
}

variables {
  before: "1991-02-19"
}

# 字面量參數
# 引擎將調用 parseLiteral 函數
query {
  users(before: "1991-02-19") {
    id
    name
  }
}

最後說一些注意的點:code

  1. 若是類型肯定不會做爲 InputType,能夠省略 parseValueparseLiteral
  2. parseValue 接收到的是 variables 對象中對應的值;而 parseLiteral 接收的則是引擎從 query 語句中解析出的 AST 節點。AST 節點內容形如:
{
  // 字面量類型
  "kind": "StringValue",
  // 字面量值
  "value": "1991-02-19",
  // 指明字面量是否爲 [BlockStringValue](https://facebook.github.io/graphql/June2018/#BlockStringValue()) 類型
  "block": false,
  // token 位置
  "loc":
  {
    "start": 18,
    "end": 30
  }
}

3. 返回對象的標量

標量類型也支持返回結構化的對象,只要能爲引擎提供符合規則的 serialize 函數,一切皆有可能。咱們能夠寫出這樣一個標量:對象

// Address 對象類型,不過這是一個標量
new GraphQLScalarType({
  name: "Address",
  description: "對象類型的標量",
  serialize(value) {
    // value 爲對象類型
    // value = { city: '深圳', province: '廣東省', country: '中國' }
    return value;
  }
});

可是要注意,標量類型是 不可分割 的,不能再傳入查詢子集:

# 合法請求
query {
  users {
    id
    name
    # Address 類型值
    bornOrigin
  }
}

返回結果:

{
  "data": {
    "users": [
      {
        "id": "1",
        "name": "foo",
        "bornOrigin": {
          "city": "深圳",
          "province": "廣東省",
          "country": "中國"
        }
      }
    ]
  }
}

完整代碼在 此處
能作是一回事,該不應這麼作,又是另外一回事,返回對象的標量雖然合乎規則,但卻違反了 按需加載 這一重要原則,沒有實際意義也不值得推崇。

總結

標量是 GraphQL 中的原子類型,通常充當查詢的葉子節點。
GraphQL 規範提供了五種標量類型,其中 ID 最爲特殊,用於惟一標誌一個資源實例。在標準標量以外,也能夠按需定義新的標量,規則如上。

相關文章
相關標籤/搜索