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還年輕,其餘它愈來愈好!

相關文章
相關標籤/搜索