graphql 新API 开发方式

咱们知道 GraphQL 使用 Schema 来描述数据,并经过制定和实现 GraphQL 规范 定义了支持 Schema 查询的 DSQL (Domain Specific Query Language,领域特定查询语言)。Schema 帮助将复杂的业务模型数据抽象拆分红细粒度的基础数据结构,而 DSQL 的实现则赋予了前端开发者自由组织和定制请求数据的能力。若是以一张图来表示的话,能够将 GraphQL 看作一条以 通用基础业务数据模型 为基础、将传统后端服务和前端页面紧密且自由地联系在一块儿的纽带。javascript

为何 GraphQL 的 Schema 可以表示出服务器所支持的复杂业务模型数据,GraphQL 的 Query 又是怎样赋予前端开发者对数据的定制能力,本文将经过分析和理解 GraphQL 的设计来和你们一块儿探讨解答这些问题。php

1.GraphQL 的设计

GraphQL 由如下组件构成:前端

  • 类型系统(Type System)
  • 查询语言(Query Language)
  • 执行语义(Execution Semantics)
  • 静态验证(Static Validation)
  • 类型检查(Type Introspection)

做为将数据模型和具体接口实现解耦的 DSL,GraphQL 的基础组件,也是它最重要的组件之一就是类型系统。java

1.1 类型系统

能够将 GraphQL 的类型系统分为标量类型(Scalar Types,标量类型)和其余高级数据类型,标量类型便可以表示最细粒度数据结构的数据类型,能够和 JavaScript 的原始类型对应。GraphQL 规范目前规定支持的标量类型有:git

  • Int :整数,对应 JavaScript 的 Number
  • Float :浮点数,对应 JavaScript 的 Number
  • String :字符串,对应 JavaScript 的 String
  • Boolean :布尔值,对应 JavaScript 的 Boolean
  • ID :ID 值,是一个序列化后值惟一的字符串,能够视做对应 ES 2015 新增的 Symbol

Scalar Types 的 JavaScript 参考实现代码能够查看 这里 。github

其余高级数据类型包括:数据库

  • Object :对象后端

    用于描述层级或者树形数据结构。对于树形数据结构来讲,叶子字段的类型都是标量数据类型。几乎全部 GraphQL 类型都是对象类型。Object 类型有一个 name 字段,以及一个很重要的 fields 字段。fields 字段能够描述出一个完整的数据结构。例如一个表示地址数据结构的 GraphQL 对象为:服务器

    const AddressType = new GraphQLObjectType({ name: 'Address', fields: { street: { type: GraphQLString }, number: { type: GraphQLInt }, formatted: { type: GraphQLString, resolve(obj) { return obj.number + ' ' + obj.street } } } }); 
  • Interface :接口数据结构

    接口用于描述多个类型的通用字段,例如一个表示实体数据结构的 GraphQL 接口为:

    const EntityType = new GraphQLInterfaceType({ name: 'Entity', fields: { name: { type: GraphQLString } } }); 
  • Union :联合

    联合类型用于描述某个字段可以支持的全部返回类型以及具体请求真正的返回类型,例如一个表示宠物(能够是猫或者狗)的 GraphQL 联合类型为:

    const PetType = new GraphQLUnionType({ name: 'Pet', types: [DogType, CatType], resolveType(value) { if (value instanceof Dog) { return DogType; } if (value instanceof Cat) { return CatType; } } }); 
  • Enum :枚举

    用于表示可枚举数据结构的类型,例如表示 RGB 色值的 GraphQL 枚举类型为:

    const RGBType = new GraphQLEnumType({ name: 'RGB', values: { RED: { value: 0 }, GREEN: { value: 1 }, BLUE: { value: 2 }, } }); 
  • Input Object :输入对象

    是为了查询(query)而定义的数据类型,不直接重用 Object 类型是由于 Object 的字段可能存在循环引用,或者字段引用了不能做为查询输入对象的接口和联合类型。参考实现中 Input Object 的定义代码为:

    export type GraphQLInputType =
      GraphQLScalarType |
      GraphQLEnumType |
      GraphQLInputObjectType |
      GraphQLList<GraphQLInputType> |
      GraphQLNonNull<
        GraphQLScalarType |
        GraphQLEnumType |
        GraphQLInputObjectType |
        GraphQLList<GraphQLInputType>
      >;
    
    export function isInputType(type: ?GraphQLType): boolean { const namedType = getNamedType(type); return ( namedType instanceof GraphQLScalarType || namedType instanceof GraphQLEnumType || namedType instanceof GraphQLInputObjectType ); } 

    能够看到,Object、Interface 和 Union 三种类型是不能做为输入对象类型的。

  • List :列表

    列表是其余类型的封装,一般用于对象字段的描述。例以下面 PersonType 类型数据的 parents 和 children 字段:

    const PersonType = new GraphQLObjectType({ name: 'Person', fields: () => ({ parents: { type: new GraphQLList(Person) }, children: { type: new GraphQLList(Person) }, }) }); 
  • Non-Null :不能为 Null

    Non-Null 强制类型的值不能为 null,而且在请求出错时必定会报错。能够用于必须保证值不能为 null 的字段。例如数据库的行的 id 字段不能为 null:

    const RowType = new GraphQLObjectType({ name: 'Row', fields: () => ({ id: { type: new GraphQLNonNull(GraphQLString) } }) }); 

还有一种重要的数据类型,即 schema 类型,它描述了后端服务器可以提供的数据支持。这里先暂时不介绍,由于它涉及 GraphQL 的其余组件,等所有介绍完咱们再来看 GraphQL 中 schema 的 具体实现 。

1.2 查询语言

类型系统对应咱们开头提到的 Schema,是对服务器端数据的描述,而查询语言则解耦了前端开发者与后端接口的依赖。前端开发者利用查询语言能够自由地组织和定制系统可以提供的业务数据。

GraphQL 的一个查询请求被称为一份 query 文档(query document),即 GraphQL 服务可以解析验证并执行的一串请求字符串。query 由操做(Operation)和片断(Fragments)组成。一个 query 能够包含多个操做和片断。只有包含操做的 query 才会被 GraphQL 服务执行。可是不包含操做,只有片断的 query 也会被 GraphQL 服务解析验证,这样一份片断就能够在多个 query 文档内使用。

只包含一个操做的 query 能够不带操做名称或者使用简写形式(即 query 关键字加操做名)。query 包含多个操做时,全部操做都必须带上名称。

操做(Operations)

GraphQL 规范支持两种操做:

  • query:仅获取数据(fetch)的只读请求
  • mutation:获取数据后还有写操做的请求

在官方提供的参考实现中咱们会发现还支持一种操做 subscription ,这是为了处理订阅更新这种比较复杂的实时数据更新场景而设计的操做,不过目前这种操做还处于试验阶段,不建议在生产环境中使用。

查询请求的模型能够用下面的图来表示:

选择集合(Selection Sets)

选择集合表示当前选中的数据内容,格式为:

{
  Field           // 字段名
  FragmentSpread  // 片断展开 InlineFragment // 内联片断 } 

关于选择集合的使用,能够参考 graphql-js 的代码 。参考实现代码在 这里 。

字段(Field)

字段格式为:

alias:name(argument:value)

其中 alias 是字段的别名,即结果中显示的字段名称。

name 为字段名称,对应 schema 中定义的 fields 字段名。

argument 为参数名称,对应 schema 中定义的 fields 字段的参数名称。

value 为参数值,值的类型对应标量类型的值。

例如这样的请求: http://yunhe.taobao.com/?query={banner{backgroundURL:bg,biaoti:slogan}} 
backgroundURL 就是 bg 字段的别名。

片断(Fragment)

片断是 GraphQL 的主要组合数据结构,经过片断能够重用重复的字段选择,减小 query 中的重复内容。片断又分为 FragmentSpread 和 InlineFragment。例如没有片断时须要这样编写 query:

query noFragments {
  user(id: 4) {
    friends(first: 10) {
      id
      name
      profilePic(size: 50)
    }
    mutualFriends(first: 10) {
      id
      name
      profilePic(size: 50)
    }
  }
}

query 中存在下列重复的选择集合:

{
  id
  name
  profilePic(size: 50)
}

能够用片断简化为:

query withFragments {
  user(id: 4) {
    friends(first: 10) {
      ...friendFields
    }
    mutualFriends(first: 10) {
      ...friendFields
    }
  }
}

fragment friendFields on User {
  id
  name
  profilePic(size: 50)
}

使用片断时须要加上 ... 操做符表示展开片断内容。

内联片断示例以下:

query inlineFragmentTyping {
  profiles(handles: ["zuck", "cocacola"]) {
    handle
    ... on User {
      friends {
        count
      }
    }
    ... on Page {
      likers {
        count
      }
    }
  }
}

指令(Directives)

指令要解决的是 query 执行时字段参数没法覆盖的状况,例如引入或者忽略某个字段。指令为 GraphQL 执行添加了更多的信息。

指令实例以下:

query hasConditionalFragment($condition: Boolean) {
  ...maybeFragment @include(if: $condition) } fragment maybeFragment on Query { me { name } } 

include 指令表示只有在 if 参数为 true 时才引入片断表示的字段。

skip 指令表示在 if 参数为 true 时忽略片断中的字段。

熟悉了 类型系统 和 查询语言 咱们就能够用 GraphQL 来实现应用层的数据请求了。其余三个 GraphQL 组件更偏向于 DSL 的实现和原理,所以本文再也不作详细介绍,感兴趣的同窗能够对照 规范 和 参考实现 本身研究。

2.总结

GraphQL 是在应用层对业务数据模型的抽象,是对数据请求定制的 DSQL,它解除了接口和数据之间的绑定,对业务数据结构作了抽象和整理,业务逻辑中的数据依赖于底层数据库结构,而且能够由具体业务场景来定制,不一样的业务场景只要基于一样一套基础业务数据模型就能够获得复用,在我看来,这才是 GraphQL 带来的最大改变和收益。

相关文章
相关标签/搜索