头图

GraphQL 入门指南

PingCode研发中心
本文由智能化研发管理工具PingCode技术经理 龚林杰分享

什么是 GraphQL

A query language for your APIs

GraphQL 是一种由 Facebook 提出的用于 API 的查询语言,也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余

image.png

常用的API有哪些

什么是API

API 全称:Application programming interface, API的本质就是读数据、写数据。

我们常用的API分成两个流派

  • 基于REST(Representational State Transfer)

REST:表现层状态转移,是一种软件架构风格,而不是一个标准。REST 用 HTTP 封装,走的是 HTTP 协议。那么给予 REST 有哪些衍生品?RESTFUL: 操作资源 ,GraphQL: A query language for your APIs

  • 基于RPC

用自定义的协议 http, http2, 消息队列(redis), tcp 等封装的一套远程调用的API,很典型的一个技术就是gRPC.

为什么要使用GraphQL

REST API 存在的问题以及怎么应对

  • POST / PUT / DELETE 傻傻分不清楚,有时候界限模糊
  • 由于是操作资源路由有时候会很长,如 /work-items/:id/comments/:cid/like
  • 数据返回冗余,叫 Overfetching
  • 数据返回缺少一些字段,叫 Underfetching
  • 接口数据如何兼容不同平台

我们在使用REST接口时,接口返回的数据格式、数据类型都是后端预先定义好的,如果返回的数据格式并不是调用者所期望的,作为前端的我们可以通过以下两种方式来解决问题:

  • 和后端沟通,改接口(更改数据源)
  • 自己做一些适配工作(处理数据源)
  • 后端专门为其他平台(移动端)提供接口

GraphQL 的优势在哪

借助 GraphQL,组织内的不同客户端应用程序可以轻松地仅查询所需数据,这一点超越了其它 REST 方法,并带来了实际应用程序性能的提高。使用传统的 REST API 端点,客户端应用程序将详询服务器资源,并接受包含了与请求匹配的所有数据的响应。如果来自 REST API 端点的成功响应返回 35 个字段,那么客户端应用程序就会收到 35 个字段。

  • GraphQL API 有强类型 schema
  • 按需获取
  • GraphQL支持快速产品开发
  • Composing GraphQL API
  • 丰富的开源生态和社区

GraphQL 的概念和示例

Schema 和 类型 (Schemas and Types)

  • 类型系统(Type System)

GraphQL 查询语言基本上就是关于选择对象上的字段

因为一个 GraphQL 查询的结构和结果非常相似,因此即便不知道服务器的情况,你也能预测查询会返回什么结果。但是一个关于我们所需要的数据的确切描述依然很有意义,我们能选择什么字段?服务器会返回哪种对象?这些对象下有哪些字段可用?这便是引入 schema 的原因。

每一个 GraphQL 服务都会定义一套类型,用以描述你可能从那个服务查询到的数据。每当查询到来,服务器就会根据 schema 验证并执行查询。

query {
  blogPosts {
    title
  }
}
  • 类型语言(Type Language)
GraphQL 服务可以用任何语言编写,因为我们并不依赖于任何特定语言的句法句式来与GraphQL schema 沟通。我们定义了自己的简单语言,称之为 “GraphQL schema language” —— 它和 GraphQL 的查询语言很相似,让我们能够和 GraphQL schema 之间可以无语言差异地沟通
  • 对象类型和字段(Object Types and Fields)
一个 GraphQL schema 中的最基本的组件是对象类型,它就表示你可以从服务上获取到什么类型的对象,以及这个对象有什么字段。使用 GraphQL schema language,我们可以这样表示它
  • 参数(Arguments)
  • 查询和变更类型(The Query and Mutation Types)
schema {
  query: Query
  mutation: Mutation
}
  • 标量类型(Scalar Types)
一个对象类型有自己的名字和字段,而某些时候,这些字段必然会解析到具体数据。这就是标量类型的来源

GraphQL 自带一组默认标量类型:

  1. Int:有符号 32 位整数。
  2. Float:有符号双精度浮点值。
  3. String:UTF‐8 字符序列。
  4. Boolean:true 或者 false。
  5. ID:ID 标量类型表示一个唯一标识符



  • 枚举类型(Enumeration Types)
enum BlogType {
  TECH
  NEWS
  ...
}
  • 列表和非空(Lists and Non-Null)
对象类型、标量以及枚举是 GraphQL 中你唯一可以定义的类型种类。但是当你在 schema 的其他部分使用这些类型时,或者在你的查询变量声明处使用时,你可以给它们应用额外的类型修饰符来影响这些值的验证
myField: [String!]
          
query BlogPostWithComment($id: ID!) {
    blogPost(id: $id) {
        title
        ... on BlogPost {
          description
        }
    }
}
  • 接口(Interfaces)
跟许多类型系统一样,GraphQL 支持接口。一个接口是一个抽象类型,它包含某些字段,而对象类型必须包含这些字段,才能算实现了这个接口
  • 联合类型(Union Types)
export type GraphQLType =
  | GraphQLScalarType
  | GraphQLObjectType
  | GraphQLInterfaceType
  | GraphQLUnionType
  | GraphQLEnumType
  | GraphQLInputObjectType
  | GraphQLList<any>
  | GraphQLNonNull<any>;
  • 输入类型(Input Types)
export default new GraphQLInputObjectType({
    name: "BlogPostInput",
    fields: {
        _id: { type: GraphQLID },
        title: { type: GraphQLString },
        description: { type: GraphQLString },
    },
});

查询和变更 (Queries and Mutations)

  • 字段(Fields)
简单而言,GraphQL 是关于请求对象上的特定字段
query {
  blogPosts {
    title
  }
}


{
  "data": {
    "blogPosts": [
      "title": "R2-D2"
    ]
  }
}
  • 参数(Arguments)
query {
  blogPosts(id: "xxx") {
    _id
    title
  }
}

{
  "data": {
    "blogPosts": [
      "_id": "xxx"
      "title": "R2-D2"
    ]
  }
}
  • 别名(Aliases)
query {
  blogs: blogPosts(id: "xxx") {
    title
  }
}

{
  "data": {
    "blogs": [
      "_id": "xxx"
      "title": "R2-D2"
    ]
  }
}
  • 片段(Fragments)
GraphQL可复用单元,可以避免重复代码
query {
  one: blogPost(id: "59507b70729885c9097720fc") {
    ...commonFields
  }
  two: blogPost(id: "59507b8b729885c9097720fe") {
    ...commonFields
    _id
    description
  }
}

fragment commonFields on BlogPost {
  title
}
  • 操作名称(Operation name)
query BlogPostOperationName {
  blogPosts {
    title
    description
  }
}
  • 变量(Variables)
query BlogPostOperationName($id: ID = "59507b70729885c9097720fc") {
  blogPost(id: $id) {
    title
  }
}
  • 指令(Directives)
query BlogPostOperationName($id: ID!, $withDescription: Boolean!) {
  blogPost(id: $id) {
    title
    description @include(if: $withDescription)
  }
}

variables {
  "id": "59507b70729885c9097720fc",
  "withDescription": false
}
  • 变更(Mutations)
mutation {
    addBlogPost (data: {title: "abcdefg", description: "abcdefg"}) {
        title
        description
    }
}
  • 内联片段(Inline Fragments)
query BlogPostWithComment($id: ID!) {
    blogPost(id: $id) {
        title
        ... on BlogPost {
          description
        }
    }
}

验证 (Validation)
通过使用类型系统,你可以预判一个查询是否有效。这让服务器和客户端可以在无效查询创建时就有效地通知开发者,而不用依赖运行时检查。

执行 (Execution)
一个 GraphQL 查询在被验证后,GraphQL 服务器会将之执行,并返回与请求的结构相对应的结果,该结果通常会是 JSON 的格式
GraphQL 不能脱离类型系统处理查询

内省 (Introspection)
我们有时候会需要去问 GraphQL Schema 它支持哪些查询。GraphQL 通过内省系统让我们可以做到这点

{
  __schema {
    types {
      name
    }
  }
}

{
  __schema {
    queryType {
      name
    }
  }
}

GraphQL 最佳实践

GraphQL 规范特意忽略了一些面向 API 的重要问题,例如处理网络、授权和分页。这并不意味着在使用 GraphQL 时没有针对这些问题的解决方案,只是因为它们并非 GraphQL 定义中的一部分,可代以工程上通行的做法来实现。
  • HTTP
https://graphql.bootcss.com/l...
  • JSON(使用 GZIP 压缩)
GraphQL 服务通常返回 JSON 格式的数据,但 GraphQL 规范 并未要求这一点。对于期望更好的网络性能的 API 层来说,使用 JSON 似乎是一个奇怪的选择,但由于它主要是文本,因而在 GZIP 压缩后表现非常好
  • 版本控制
虽然没有什么可以阻止 GraphQL 服务像任何其他 REST API 一样进行版本控制,但 GraphQL 强烈认为可以通过 GraphQL schema 的持续演进来避免版本控制
  • 分页
https://graphql.bootcss.com/l...
  • 服务器端的批处理与缓存
https://graphql.bootcss.com/l...

两种数据请求方式

  • Queries
  • Mutations
    image.png





示例代码地址
graphql-koa-demo

资料
GraphQL 规范 https://spec.graphql.cn/
GraphQL 中文文档 https://graphql.cn/

对于前端不同框架也都有关于Graphql相关的库

  • angular

官网: https://www.apollographql.com/
库: https://github.com/kamilkisie...

  • vue

网站文档: https://vue-apollo.netlify.app/
库: https://github.com/Akryum/vue...

  • react

库: https://github.com/apollograp...

阅读 411
1 声望
2 粉丝
0 条评论
你知道吗?

1 声望
2 粉丝
宣传栏