GraphQL 实战:Github V4 API使用

作者: 一字马胡
转载标志 【2017-11-13】

更新日志

日期 更新内容 备注
2017-11-13 新建文章 初版

导入

作为一种强大的DSQL,学习GraphQL的意义是非常大的,为了迅速了解GraphQL,可以参考文章GraphQL初探:一种强大的DSQL,该文章给出了一个使用GraphQL的简单示例demo。GraphQL不仅可以面向传统的数据库(比如Mysql,NoSql),还可以面向微服务,以及其他的数据源,或者面向数据库、微服务等混合数据源,GraphQL试图抽象服务端的数据成为一个综合的数据库,而前端(客户端)的请求就是查询数据库,这样做很明显的一个好处是前端(客户端)可以根据自己的需要来查询(就像是请求mysql数据库,只输出需要的列),这称为“精准查询”,使用GraphQL的另外一个明显的好处是一次请求即可获取所需要的数据,对于请求REST API来说,每次请求返回的数据格式是相对固定的,没有太多的灵活性,并且可能为了将接口变薄,会根据数据属性等将接口拆分开来,所以每个接口可能只能返回一部分需要的内容,所以就需要客户端来请求多次来获取到所有的数据,这样就造成了一些延时。下面的图片展示了GraphQL如何将多种数据源组合起来为前端(客户端)提供统一的API(使用GraphQL来设计API只需要提供一个端点):

本文依然不会对GraphQL的原理做太多分析总结,学习需要一步一步来,上一篇文章仅仅写了一个可运行的demo,本文想要借助现有的实现来更加深入的了解一些关于GraphQL的内容,为后面可以自主设计GraphQL API提供支持。Github 的V4 API借助了GraphQL来设计,具有很高的研究价值,所以本文就介绍Github 的V4 API,并且介绍如何使用V4接口来从Github的服务器获取我们想要的数据,因为Github V4 API只提供了一个端点:https://api.github.com/graphql,所以很大程度上降低了我们的认知障碍,并且Github提供了在线查询界面:Github V4 API Explorer,你可以在左侧的输入框中输入GraphQL查询语句,点击执行就可以在右侧看到执行结果,非常直观,并且提供了完整的接口文档,可以获取任意我们想要获取的数据,本文将首先介绍一些Github V4 API中的新概念,然后基于Github V4 API Explorer来介绍如何获取我们想要的数据。

Github V4 API中的一些新概念

Schema

首先是Schema,在GraphQL中Schema是一个很重要的概念,Schema定义了GraphQL API的类型系统,它完整的描述了前端(或者客户端)可以从服务端获取的所有数据内容,前端或者客户端的GraphQL查询请求将根据Schema进行校验和执行,客户端可以通过“自省”(introspection)获取关于schema的信息。schema存放于GraphQL API服务器。关于如何生成一个schema可以参考本文开篇的文章链接。

Field

Field是客户端可以从服务端获取的某个内容,比如某个字段,或者某个对象等,GraphQL查询语言本质上就是从对象中选择field,所有的GraphQL操作必须指明到最底层的field,并且返回值为标量,以确保响应结果的结构明白无误,所谓标量(scalar),也就是基本数据类型,比如String、int等。如果你尝试返回一个不是标量的field,schema校验将会抛出错误。你必须添加嵌套的内部field直至所有的field都返回标量,这一点很重要,需要牢牢记住。

Connection

还记得GraphQL的标志吗?它是一个图,由顶点和边构成,所谓Connection,就是可以关联查询,GraphQL就是通过Connection来实现只需要一次请求就可以获取全部需要的数据的功能的,客户端可以通过Connection来进行关联查询,比如在查找一篇文章的信息的时候,除了文章的标题、作者、文字内容、图片内容、阅读数等信息外,可以通过Connection查询该文章下面的评论信息,而在评论中,可以通过Connection根据评论人的id来获取用户的信息,这就是Connection的威力,下文中将会把Connection的威力体现出来。

Edge

Edge是GraphQL中的边,它表示Node之间的Connection,当你查询一个connection时,你通过edge到达node。每个edgesfield都有一个nodefield和一个cursorfield。cursor是用来分页的。

Node

Node是一个对象,它就是我们获取数据的节点,比如用户信息的对象就是一个节点,或者一篇文章的信息就是一个节点,如果正在查询的Node不是标量的话,那么我们需要指定Node中的Field直到返回的是标量类型为止。

Github V4 API实战

上文中介绍了一些关于Github V4 API的相关内容,本文剩下的部分将清楚明白的介绍如何使用Github V4 API来获取我们想要的数据,通过本文的介绍,你应当很清晰的认识到GraphQL带来的福利,以及Github V4 API为何要选择GraphQL来进行设计。首选,你需要打开Github V4 API Explorer,下面的图片展示了你应当看到的界面:

左侧是GraphQL查询语句的输入框,中间是执行结果,右侧是GraphQL查询文档,配合这三个区域,我们可以很方便的获取任意我们想要获取的数据,下面由浅入深的介绍如何从Github获取数据。首先是最简单的一个例子,我想获取当前登录的账号的一些基本信息,包括账号名字,邮箱,id,下面是查询语句:


query {
  viewer {
    login
    url
    id
  }
}

查询的结果如下:


{
  "data": {
    "viewer": {
      "login": "pandening",
      "url": "https://github.com/pandening",
      "id": "MDQ6VXNlcjE2MjI1Nzk2"
    }
  }
}

如果现在我想要获取我的签名呢?只需要在查询语句中增加一个Field就可以了:


query {
  viewer {
    login
    url
    id
    bio
  }
}

返回的结果如下:


{
  "data": {
    "viewer": {
      "login": "pandening",
      "url": "https://github.com/pandening",
      "id": "MDQ6VXNlcjE2MjI1Nzk2",
      "bio": "/╲/\\╭༼ ººل͟ºº ༽╮/\\╱\\"
    }
  }
}


从返回的结果可以看出,我的登录账号的名字叫做pandening,我的主页url为https://github.com/pandening,id为MDQ6VXNlcjE2MjI1Nzk2,我的签名为“/╲/\╭༼ ººل͟ºº ༽╮/\╱\”(非主流啊),可以发现返回的json和查询的输入是一对一的,这就是所谓的精准查询,不多不少,正好合适。那现在来说说如何写一个查询呢?比如我上面的查询语句是怎么写出来的呢?这就的配合右侧的接口文档了,点开文档可以看到有两种类型的操作,一种是Query,用于查询数据,另外一种是Mutation,用于向服务端发送写请求,本文主要关注的是读数据,也就是Query操作,关于Mutation的相关内容也是类似的。点开Query,我们可以看到下面的内容:

上面我使用了viewer来查询当前登录的Github的信息,所以点开viewer,就可以看到我们可以获取的Field了,如果该Field为标量类型,那么可以直接返回,比如id,如果某个Field不是标量的,比如followers,它就不是一个标量,那么就要一直查询直到返回标量为止。上面的例子很简单,但是通过这个例子可以开始结合接口文档来进行复杂的查询了,下面,有一个需求,需要获取当前账号的名字,url,签名,加入Github的时间,邮箱,以及该账号的前5个followers(账号名、bio、邮箱、加入Github的时间),当前账号的前5个仓库(名字、star信息、fork信息),下面是构造出来的GraphQL查询语句:


query {
  viewer {
    login
    url
    bio
    email
    createdAt
    followers(first : 5) {
      edges {
        node {
          name
          bio
          email
          createdAt
        }
      }
    }
    repositories(first : 5, isFork : false) {
      edges {
        node {
          name
          stargazers (first : 10){
            edges {
              starredAt
              node {
                name
              }
            }
          }
          forks (first : 10){
            edges {
              node {
                createdAt
                name
              }
            }
          }
        }
      }
    }
  }
}


返回的结果如下:


{
  "data": {
    "viewer": {
      "login": "pandening",
      "url": "https://github.com/pandening",
      "bio": "/╲/\\╭༼ ººل͟ºº ༽╮/\\╱\\",
      "email": "1425124481@qq.com",
      "createdAt": "2015-12-09T14:17:52Z",
      "followers": {
        "edges": [
          {
            "node": {
              "name": "Wang Weitao",
              "bio": "",
              "email": "softweitao@126.com",
              "createdAt": "2012-10-16T16:02:17Z"
            }
          },
          {
            "node": {
              "name": "Nthan",
              "bio": "",
              "email": "664157212@qq.com",
              "createdAt": "2013-01-16T02:21:07Z"
            }
          },
          {
            "node": {
              "name": "Mateusz Bagiński",
              "bio": "Python/JavaScript Developer",
              "email": "cziken58@gmail.com",
              "createdAt": "2013-03-23T12:49:03Z"
            }
          },
          {
            "node": {
              "name": "Dalin Huang",
              "bio": "these violent delights have violent ends",
              "email": "dhuan023@gmail.com",
              "createdAt": "2014-01-26T20:07:36Z"
            }
          },
          {
            "node": {
              "name": "Ramsey",
              "bio": "Arsenal Fan",
              "email": "kiminh@sjtu.edu.cn",
              "createdAt": "2014-07-18T13:01:22Z"
            }
          }
        ]
      },
      "repositories": {
        "edges": [
          {
            "node": {
              "name": "HJSTL",
              "stargazers": {
                "edges": [
                  {
                    "starredAt": "2016-05-08T04:12:01Z",
                    "node": {
                      "name": "Jian Hu"
                    }
                  }
                ]
              },
              "forks": {
                "edges": [
                  {
                    "node": {
                      "createdAt": "2017-01-11T02:14:01Z",
                      "name": "hjstl"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "name": "AcppLib",
              "stargazers": {
                "edges": [
                  {
                    "starredAt": "2016-08-21T03:33:14Z",
                    "node": {
                      "name": "Jian Hu"
                    }
                  }
                ]
              },
              "forks": {
                "edges": []
              }
            }
          },
          {
            "node": {
              "name": "poj-solution",
              "stargazers": {
                "edges": [
                  {
                    "starredAt": "2016-08-22T12:20:41Z",
                    "node": {
                      "name": "Jian Hu"
                    }
                  }
                ]
              },
              "forks": {
                "edges": [
                  {
                    "node": {
                      "createdAt": "2016-12-29T21:18:04Z",
                      "name": "poj-solution"
                    }
                  }
                ]
              }
            }
          },
          {
            "node": {
              "name": "storm-example-onlineusers",
              "stargazers": {
                "edges": [
                  {
                    "starredAt": "2016-08-22T12:24:23Z",
                    "node": {
                      "name": "Jian Hu"
                    }
                  }
                ]
              },
              "forks": {
                "edges": []
              }
            }
          },
          {
            "node": {
              "name": "storm-jsoup-spider",
              "stargazers": {
                "edges": [
                  {
                    "starredAt": "2016-08-24T04:40:58Z",
                    "node": {
                      "name": "Jian Hu"
                    }
                  }
                ]
              },
              "forks": {
                "edges": []
              }
            }
          }
        ]
      }
    }
  }
}

看着很复杂,但是只要照着右侧的GraphQL接口文档就可以快速的组装出我们需要的数据了,当然,你可以组装任意复杂的查询语句,只要符合GraphQL的查询语句的要求,都可以从Github 获取数据,但是需要注意的一点是,GraphQL确实可以一次性获取很全面的数据,但是也需要考虑响应时间的问题,不能一次性获取了大量的数据,但是延时很高,这样的应用的用户体验是很差的,需要权衡一下。

结语

本文是关于GraphQL系列的第二篇文章,主要介绍了Github的V4 API中的一些新概念,以及如何使用V4 API来获取我们想要的数据,文中首先介绍了一个比较简单的查询,然后是一个相对复杂的查询,并且介绍了如何依靠Github提供的GraphQL接口文档来设计自己的GraphQL查询语句,根据Github提供的GraphQL文档,我们可以从Github获取足够复杂的数据。Github的API设计一直以来都是业界的标杆,包括V3的Rest接口,以及V4的GraphQL接口,都值得仔细学习。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 141,558评论 1 298
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 60,739评论 1 254
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 93,327评论 0 211
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 40,752评论 0 174
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 48,452评论 1 252
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,617评论 1 171
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,286评论 2 267
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,083评论 0 165
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 28,839评论 6 227
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,413评论 0 213
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,186评论 2 213
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,506评论 1 223
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,171评论 0 31
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,049评论 2 213
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,417评论 3 202
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,588评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 25,942评论 0 163
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,392评论 2 228
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,499评论 2 229

推荐阅读更多精彩内容