Go — 搭建GraphQL 服务端

Github提供的GraphQL接口很是全面,那么咱们该如何搭建出本身的接口呢?好在GraphQL提供了不少语言的解决方案。本文主要阐述如何用go搭建本身的GraphQL服务器。若是了解GraphQL建议先阅读GraphQL — API查询语言 或相关资料。python

graphql-go

An implementation of GraphQL in Go. Follows the official reference implementation graphql-js.

一套比较完善的框架,众所周知go的结构体对json很是友好,因此并不须要对数据有特殊的处理,仍是很方便的。打开终端输入命令git

go get github.com/graphql-go/graphqlgithub

Object

在服务端编程中,编写的一切均可以称之为对象(Object)。例如一个商品(goods)的实例能够有商品名(name)、价格(price)、购买连接(url)三个字段。此时商品能够很天然的被称为一个object,查询的语句能够写成:golang

{
    goods{
        name
        price
        url
    }
}

若是此时咱们要查询商品和文章两种object的信息:编程

/* query 能够省去 */
query{ 
    goods{
        name
    }
    article{
        name
    }
}

是否你已经发觉,query像一个大的object,它有goods和article两个字段。除此以外,mutation也是如此:json

mutation{
    addGoods(input:goodsInput){
        name
    }
}

这里的addGoods能够看作是一个能够处理参数的对象,也就是某种意义上的函数api

总之,GraphQL服务端的编程就是一个又一个的对象将造成的嵌套结构(schema)组织起来,并对外提供服务。数组

query&mutation

为了防止低级错误的发生,在当前pkg下新建一个名为query.go(随便起)的文件。浏览器

import (
    "github.com/graphql-go/graphql"
    "errors"
)

定义good object服务器

type Goods struct {
    ID    string `json:"id"`
    Name  string `json:"name"`
    Price float64`json:"price"`
    Url   string `json:"url"`
}

var goodsType = graphql.NewObject(
    graphql.ObjectConfig{
        Name: "Goods",
        Fields: graphql.Fields{
            "id": &graphql.Field{
                Type: graphql.String,
            },
            "name": &graphql.Field{
                Type: graphql.String,
            },
            "price": &graphql.Field{
                Type: graphql.Float,
            },
            "url": &graphql.Field{
                Type: graphql.String,
            },
        },
    },
)
var goodsListType = graphql.NewList(goodsType)

注意:数组至关于新的object类型。

定义query object

var queryType = graphql.NewObject(
    graphql.ObjectConfig{
        Name: "Query",
        Fields: graphql.Fields{
            // 无需处理参数
            "goodsList": &graphql.Field{
                Type:goodsListType,
                // 处理结构体的回调函数,直接返回处理完成的结构体便可
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    return result, err
                },
            },
            // 参数是id
            "goods": &graphql.Field{
                Type: goodsType,
                Args: graphql.FieldConfigArgument{
                    "id": &graphql.ArgumentConfig{
                        Type: graphql.String,
                    },
                },
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    // 获取参数
                    idQuery, isOK := p.Args["id"].(string)
                    if isOK {
                        return result, nil
                    }
                    err := errors.New("Field 'goods' is missing required arguments: id. ")
                    return nil, err
                },
            },
        },
    },
)

mutation定义基本相同,新建一个名为mutation.go的文件:

定义input object

var goodsInputType = graphql.NewInputObject(
    graphql.InputObjectConfig{
        Name: "goodsInput",
        Fields: graphql.InputObjectConfigFieldMap{
            "name": &graphql.InputObjectFieldConfig{
                Type: graphql.String,
            },
            "price": &graphql.InputObjectFieldConfig{
                Type: graphql.Float,
            },
            "url": &graphql.InputObjectFieldConfig{
                Type: graphql.String,
            },
        },
    },
)

定义 mutation object

var mutationType = graphql.NewObject(
   graphql.ObjectConfig{
      Name: "Mutation",
      Fields: graphql.Fields{
      "addGoods":&graphql.Field{
                Type:goodsType,
                Args:graphql.FieldConfigArgument{
                    "input":&graphql.ArgumentConfig{
                        Type:goodsInputType,
                    },
                },
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    input,isOk := p.Args["input"].(map[string]string)
                    if !isOk{
                        err := errors.New("Field 'addGoods' is missing required arguments: input. ")
                        return nil,err
                    }
                    result := Goods{
                        Name:input["name"].(string),
                        Price:input["price"].(float64),
                        Url:input["url"].(string),
                    }
                    // 处理数据
                    return result,err
                },
            },
        },
    },
)

然而,input类型并不能直接转换为struct,而是一个map[string]interface{}类型,还须要进行手动转换。

定义schema

var schema, _ = graphql.NewSchema(
   graphql.SchemaConfig{
      Query:    queryType,
      Mutation: mutationType,
   },
)

至此,咱们的所有的object定义完成。

提供服务

graphql-go为咱们提供了一个方便的接口,封装好的handler能够直接与go自带的http包绑定。

package api
import "github.com/graphql-go/handler"

func Register() *handler.Handler {
   h := handler.New(&handler.Config{
      Schema:   &schema,
      Pretty:   true,
      GraphiQL: true,
   })
   return h
}
func main() {
   h := api.Register()
   handler := cors.Default().Handler(h)
   http.Handle("/graphql", handler)
   fmt.Println("The api server will run on port : ", apiPort)
   http.ListenAndServe(apiPort, nil)
}

打开浏览器,访问http://localhost:apiPort/graphql, 查看你本身的GraphiQL界面吧!

结束语

若是你以为这样的代码谈不上优雅,甚至很是丑陋,那就对了。由于我也这样以为,看一看隔壁python的实现方式:

import graphene

class Query(graphene.ObjectType):
  hello = graphene.String()

  def resolve_hello(self, args, context, info):
    return 'Hello world!'

schema = graphene.Schema(query=Query)

有没有涌来一口老血。

多是受限与golang自己反射系统并不够完善,没有python各类各样的魔术方法,没有泛型,或者说go自己不太适合编写框架类的代码。在编写的过程当中,冗余很是多,固然也多是框架自己的问题

不能否认的是,go确实是很是不错的一门语言,虽然开发效率没法与python媲美,可是在多并发环境下,go表现出很是出色,同时拥有与C级别的运行速度和丰富的生态。

go还年轻,其余它愈来愈好!

相关文章
相关标签/搜索