概述

GraphQL是由FaceBook开发的一种API查询语言,调用者可以无任何冗余的情况下获取到数据。
GraphQL对标的是Rest

  • Rest是由后台定义返回的字段,如果后台返回的字段过多,则会出现返回的无用字段影响性能,如果后台返回的字段不足,则需要后台重新开发新增需要的字段
  • GraphQL是由调用者定义需要返回的字段,后台根据请求返回指定字段

学习资料

官方文档中文:https://graphql.bootcss.com/learn/

官方文档英文:https://graphql.org/learn/

官方文档的资料写的很详细也非常浅显易懂,我下面也只是搬运核心的一些内容。
(顺口一提)官方文档里面的示例是可以直接修改运行的

schema

中文文档:https://graphql.bootcss.com/learn/schema/
英文文档:https://graphql.org/learn/schema/

GraphQL是由schema文件来定义所提供的能力,简单理解就是用schema来定义有哪些接口,接口对应的请求结构体和响应接口体

官方文档上并没有给出示例的starwar的schema文件,含义也没有做解释,我这里稍微做一下补充,如下:

# The query type, represents all of the entry points into our object graph
type Query {
    hero(episode: Episode = NEWHOPE): Character
    reviews(episode: Episode!, since: Time): [Review!]!
    search(text: String!): [SearchResult!]!
    character(id: ID!): Character
    droid(id: ID!): Droid
    human(id: ID!): Human
    starship(id: ID!): Starship
}
# The mutation type, represents all updates we can make to our data
type Mutation {
    createReview(episode: Episode!, review: ReviewInput!): Review
}

# A humanoid creature from the Star Wars universe
type Human implements Character {
    # The ID of the human
    id: ID!
    # What this human calls themselves
    name: String!
    # Height in the preferred unit, default is meters
    height(unit: LengthUnit = METER): Float!
    # Mass in kilograms, or null if unknown
    mass: Float
    # This human's friends, or an empty list if they have none
    friends: [Character!]
    # The friends of the human exposed as a connection with edges
    friendsConnection(first: Int, after: ID): FriendsConnection!
    # The movies this human appears in
    appearsIn: [Episode!]!
    # A list of starships this person has piloted, or an empty list if none
    starships: [Starship!]
}
# An autonomous mechanical character in the Star Wars universe
type Droid implements Character {
    # The ID of the droid
    id: ID!
    # What others call this droid
    name: String!
    # This droid's friends, or an empty list if they have none
    friends: [Character!]
    # The friends of the droid exposed as a connection with edges
    friendsConnection(first: Int, after: ID): FriendsConnection!
    # The movies this droid appears in
    appearsIn: [Episode!]!
    # This droid's primary function
    primaryFunction: String
}
# A connection object for a character's friends
type FriendsConnection {
    # The total number of friends
    totalCount: Int!
    # The edges for each of the character's friends.
    edges: [FriendsEdge!]
    # A list of the friends, as a convenience when edges are not needed.
    friends: [Character!]
    # Information for paginating this connection
    pageInfo: PageInfo!
}
# An edge object for a character's friends
type FriendsEdge {
    # A cursor used for pagination
    cursor: ID!
    # The character represented by this friendship edge
    node: Character
}
# Information for paginating this connection
type PageInfo {
    startCursor: ID!
    endCursor: ID!
    hasNextPage: Boolean!
}
# Represents a review for a movie
type Review {
    # The number of stars this review gave, 1-5
    stars: Int!
    # Comment about the movie
    commentary: String
    # when the review was posted
    time: Time
}
# The input object sent when someone is creating a new review
input ReviewInput {
    # 0-5 stars
    stars: Int!
    # Comment about the movie, optional
    commentary: String
    # when the review was posted
    time: Time
}
type Starship {
    # The ID of the starship
    id: ID!
    # The name of the starship
    name: String!
    # Length of the starship, along the longest axis
    length(unit: LengthUnit = METER): Float!
    # coordinates tracking this ship
    history: [[Int!]!]!
}

# The episodes in the Star Wars trilogy
enum Episode {
    # Star Wars Episode IV: A New Hope, released in 1977.
    NEWHOPE
    # Star Wars Episode V: The Empire Strikes Back, released in 1980.
    EMPIRE
    # Star Wars Episode VI: Return of the Jedi, released in 1983.
    JEDI
}
# A character from the Star Wars universe
interface Character {
    # The ID of the character
    id: ID!
    # The name of the character
    name: String!
    # The friends of the character, or an empty list if they have none
    friends: [Character!]
    # The friends of the character exposed as a connection with edges
    friendsConnection(first: Int, after: ID): FriendsConnection!
    # The movies this character appears in
    appearsIn: [Episode!]!
}
# Units of height
enum LengthUnit {
    # The standard unit around the world
    METER
    # Primarily used in the United States
    FOOT
}
union SearchResult = Human | Droid | Starship
scalar Time
类型含义示例
!标识该字段非空
[[]](https://graphql.bootcss.com/l...)表示当前字段为数组类型
Query对外提供查询的接口,类似于rest中的get请求,括号中为请求结构体,最后字段响应结构体上文中Queryhero为例,请求参数名为episode,请求类型为Episode,如果不传参,默认值为NEWHOPE,响应类型为Character
Mutation对外提供的插入接口,类似于rest中的put请求,括号中为请求结构体,最后字段响应结构体上文中MutationcreateReview为例,请求参数名为episodereview,请求类型为EpisodeReviewInput,且都不能为空,响应类型为Review
type结构体,定义GraphQL中所需要的结构体上文中的Human为例,定义了id,name,height等字段,类型ID,String,Float,Boolean, IntGraphQL自身提供的类型,字段也可以有查询参数。比如height字段有参数unit,类型为LengthUnit,默认值为METER
interface接口类型,其实我觉得理解成超类或者父类更合适,就是定义了其他类型所共有的字段上文中Character为例,DroidHuman都实现(继承)了Character的属性,这里的子类需要重新声明Character的字段
enum枚举类型上文中Episode为例,定了三个值NEWHOPE,EMPIRE,JEDI,枚举的类型这里不会体现,可以在实现的时候自定义,通常是字符串类型
input定义接口的请求参数类型,该类型字段只能用户接口的入参上文中ReviewInput为例,在Mutation中的createReview的入参为ReviewInput类型
union联合类型,和接口十分相似,但是它并不指定类型之间的任何共同字段。上文中SearchResult为例,该字段既可以是Human,也可以是Droid,或者Starship,在实现的时候可以解析指定类型
scalar定义标量类型,GraphQL中默认的标量为ID,String,Float,Boolean, Int,实现的时候需要实现类型的序列化,反序列,验证等方法上文中Time为例,定义全新的标量,对应的解析方法需要在代码实现

查询

中文文档:https://graphql.bootcss.com/learn/queries/
英文文档:https://graphql.org/learn/queries/

建议去上面的网址去运行一下脚本,实地感受一下效果

query testQueryHuman{
  human1000:human(id: "1000") {
    name
    height(unit: FOOT)
  }
  human1001:human(id: "1001") {
    name
    height
  }
}

得到的效果:

{
  "data": {
    "human1000": {
      "name": "Luke Skywalker",
      "height": 5.6430448
    },
    "human1001": {
      "name": "Darth Vader",
      "height": 2.02
    }
  }
}


1、可以看到查询几个字段就返回几个字段
2、query testQueryHuman为当前查询的操作名称query为固定资,后面的名字自定义
3、human1000human1001为查询结果的别名,可以不写
4、id: "1000"id: "1001"为查询参数,即查询ID为1000和1001的人类,同时可见查询的字段height也是可以带参数的
5、其他的操作,如片段指令变更内联片段见官方文档

我理解的优缺点

优点:

  • 由调用者决定返回的数据,数据足够透明
  • 降低网络带宽,提供相应速度
  • 减少服务的开发成本,更加适应多变的需求

缺点:

  • 系统整体架构复杂化,毕竟多引入了组件
  • 后台系统复杂度上升,数据层对数据的维护和提供能力需要增强,因为原本只需要查询少量字段,现在需维护所有需要的字段,以备调用者请求
  • 对调用者而言,学习成本更大,本来只需要了解接口字段含义,现在需要了解整个GraphQL提供的能力

实现

GraphQL提供了多种语言的实现代码库,详情可见https://graphql.bootcss.com/code/

java的之前玩过了,也写过一下文档,可见https://blog.csdn.net/q15150676766/category_7004424.html但是有点年久失修了

这次主要研究的是GO使用GraphQL的框架,GO提供的框架太多了,学习起来有点吃力,这次主要学习和研究最火的三个(也是就star最多的三个),我们项目中用的是gqlgen,太高级的功能没做深究,粗浅的学习一下怎么用。


下面是大佬们整理的三个框架的比较
 title=


ncfl
1 声望1 粉丝