Go创建GraphQL API

2019 年 11 月 25 日

Go创建GraphQL API

越来越多的项目中都能看到 GraphQL 的身影,不知道大家在项目中有没有使用过 GraphQL 呢?


随着时间推移,我们在越来越多的项目中,都可以发现 GraphQL 的身影。


从几个方面看,包括图形化的数据、多个分布式团队和高度版本化的 api,以及关于类型安全和文档的问题。GraphQL 看起来非常适合许多不同的应用程序。


本文的目标不是去介绍 GraphQL 的基础知识,而是在实际场景中看到它的实际操作。当计划将现有的 REST API 移动到 GraphQL 时,需要引入一个转换层,以实现平稳过渡。


在本篇文章中,我们将使用 jsonplaceholder 作为我们用 GraphQL 包装的 API。这里有几个用于 graphQL 的库,在本篇文章示例中,将使用 graphql-go 和 graphql-go-handler。


我们的目标是从 jsonplaceholder 获取文章和评论,最后以一种通过 ID 获取文章的方式,如果 API 使用者希望获取评论,则通过 GraphQL 将评论嵌套到文章中。


让我们开始吧。


开始实现


首先,我们为文章和评论定义数据模型:


type Post struct {    UserID int    `json:"userId"`    ID     int    `json:"id"`    Title  string `json:"title"`    Body   string `json:"body"`}
type Comment struct { PostID int `json:"postId"` ID int `json:"id"` Name string `json:"name"` Email string `json:"email"` Body string `json:"body"`}
复制代码


我们定义 fetchPostByiD(id)函数,它用来调用http://jsonplaceholder.typicode.com/posts/{id}/注释中获取数据,并将其转换为[]Comment。


然后我们继续创建 graphQL 模式。我们从定义 queryType 开始,它是模式的根:


func createQueryType(postType *graphql.Object) graphql.ObjectConfig {    return graphql.ObjectConfig{Name: "QueryType", Fields: graphql.Fields{        "post": &graphql.Field{            Type: postType,            Args: graphql.FieldConfigArgument{                "id": &graphql.ArgumentConfig{                    Type: graphql.NewNonNull(graphql.Int),                },            },            Resolve: func(p graphql.ResolveParams) (interface{}, error) {                id := p.Args["id"]                v, _ := id.(int)                log.Printf("fetching post with id: %d", v)                return fetchPostByiD(v)            },        },    }}}
复制代码


根查询类型只有一个字段 —— post。这个字段是由 postType 定义的,我们稍后会看到。它只接受一个名为 id 的参数。


文章通过从 p.Args 中解析出 id,并将它传递给 fetchPostsByID,返回已获取和转换后的 Post 以及错误信息。


接下来,我们定义 postType,这很有趣。我们基本上只是把 post 字段从数据模型映射到 graphQL 类型,但我们也添加了 comments 字段。如果客户端显式地想要取回它们,那么评论的 Resolve 函数才会被调用。


为了解决评论,我们使用 p.Source 来访问这个查询的“父”。它为我们提供了一个 Post 实例*Post——已获取的文章。使用这篇文章的 id,我们可以获取评论:


func createPostType(commentType *graphql.Object) *graphql.Object {    return graphql.NewObject(graphql.ObjectConfig{        Name: "Post",        Fields: graphql.Fields{            "userId": &graphql.Field{                Type: graphql.NewNonNull(graphql.Int),            },            "id": &graphql.Field{                Type: graphql.NewNonNull(graphql.Int),            },            "title": &graphql.Field{                Type: graphql.String,            },            "body": &graphql.Field{                Type: graphql.String,            },            "comments": &graphql.Field{                Type: graphql.NewList(commentType),                Resolve: func(p graphql.ResolveParams) (interface{}, error) {                    post, _ := p.Source.(*Post)                    log.Printf("fetching comments of post with id: %d", post.ID)                    return fetchCommentsByPostID(post.ID)                },            },        },    })}
复制代码


在模式中唯一要定义的类型是 commentType,这很无聊,因为它只将数据模型的字段映射到 graphQL 类型:


func createCommentType() *graphql.Object {    return graphql.NewObject(graphql.ObjectConfig{        Name: "Comment",        Fields: graphql.Fields{            "postid": &graphql.Field{                Type: graphql.NewNonNull(graphql.Int),            },            "id": &graphql.Field{                Type: graphql.NewNonNull(graphql.Int),            },            "name": &graphql.Field{                Type: graphql.String,            },            "email": &graphql.Field{                Type: graphql.String,            },            "body": &graphql.Field{                Type: graphql.String,            },        },    })}
复制代码


好了,我们的模式被定义了,剩下的就是把它们放在一起。


我们实例化一个 graphQL 模式并将其传递给 graphql-go-handler,它是一个 http 中间件,它帮助我们处理 graphQL 查询。然后我们简单地启动一个 http 服务器,它的返回处理程序被路由到/graphql。


这像这样:


func main() {    schema, err := graphql.NewSchema(graphql.SchemaConfig{        Query: graphql.NewObject(            createQueryType(                createPostType(                    createCommentType(),                ),            ),        ),    })    if err != nil {        log.Fatalf("failed to create schema, error: %v", err)    }    handler := gqlhandler.New(&gqlhandler.Config{        Schema: &schema,    })    http.Handle("/graphql", handler)    log.Println("Server started at http://localhost:3000/graphql")    log.Fatal(http.ListenAndServe(":3000", nil))}
复制代码


好了,以上就是全部实现!


启动服务器之后,我们可以使用 GraphiQL 查询一个具有特定 id 的文章,指定我们感兴趣的字段:


query {    post(id: 5) {        userId        id        body        title        comments {            id            email            name        }    }}
复制代码


获取到以下返回信息:


{    "data": {        "post": {             "userId": 1,             "id": 5,             "title": "...",             "body": "...",                   "comments": [                 {                     "id": 21,                               "email": "...",                               "name": "..."                 },                 ...             ]        }    }}
复制代码


如果我们在查询中去掉了评论,那么就不会发出获取评论的请求,我们只是简单地将选定的帖子作为响应。还有完整的示例代码地址


总结


这个例子展示了如何使用一个简单的 Go 层将现有的 REST API 转换成 GraphQL。我们使用的这个库,graphql-go 工作得很好,提供了可靠的文档和良好的示例。


当然,有更简洁、更有趣的方法来定义这样的模式,但是由于技术等有限,我选择了这个解决方案,它的重点是清晰性。希望大家举一反三,自己结合实际去编写适合项目的实现。


本文转载自公众号 360 云计算(ID:hulktalk)。


原文链接:


https://mp.weixin.qq.com/s/UY0rixCeGfICayW6vCG2dA


2019 年 11 月 25 日 17:28307

评论

发布
暂无评论
发现更多内容

架构师训练营第1周作业

Binary

【第九周】性能优化(三)

云龙

架构师训练营第 1 期 -- 第十周作业

发酵的死神

极客大学架构师训练营

架构师训练营第 6 周学习总结

菜青虫

极客大学架构师训练营

架构师训练营第十周总结

月殇

第六章总结

孤星

架构师训练营第 1 周学习总结

Binary

【第九周】课后作业

云龙

架构师训练营第 6 周课后练习

菜青虫

极客大学架构师训练营

架构师训练营第 1 期第 10 周学习总结

好吃不贵

极客大学架构师训练营

第六章作业

孤星

架构师训练营 - 第十周作业

一个节点

极客大学架构师训练营

第六周作业

Jack

阿里面试 问我字符串

java金融

Java 面试 string 字符串

架构师训练营第 1 期 - 第 10 周学习总结

Anyou Liu

极客大学架构师训练营

架构师训练营第六周作业1

韩儿

微服务手册:API接口9个生命节点,构建全生命周期管理

互联网应用架构

微服务 APi设计 API网关

架构师训练营 - 第十周总结

一个节点

极客大学架构师训练营

架构师训练营第十周作业

月殇

极客大学架构师训练营

Week 10 作業

--------世界中心---------

架构师训练营第十周总结

听夜雨

极客大学架构师训练营

架构师训练营第 1 期第 10 周作业

好吃不贵

极客大学架构师训练营

网上系统显示部分入注数据未回传不更新该怎么解决?

Geek_a6658e

维权 互联网应用技术方案

架构师训练营第 1 期 - 第 10 周课后练习

Anyou Liu

极客大学架构师训练营

架构师训练营第六周作业2

韩儿

架构师训练营第十周作业

听夜雨

极客大学架构师训练营

架構師訓練營 week10 總結

ilake

架構師訓練營第 1 期 - 第 10 周總結

Panda

架構師訓練營第 1 期

设备日志的用途

网络技术平台

日志 snmp

复旦教授亲身编写,最新版《神经网络与深度学习》中文版开放下载

计算机与AI

神经网络 学习

Week 10 學習總結

--------世界中心---------

Go创建GraphQL API-InfoQ