這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Github提供的GraphQL介面非常全面,那麼我們該如何搭建出自己的介面呢?好在GraphQL提供了很多語言的解決方案。本文主要闡述如何用go搭建自己的GraphQL伺服器。如果瞭解GraphQL建議先閱讀GraphQL — API查詢語言 或相關資料。
graphql-go
An implementation of GraphQL in Go. Follows the official reference implementation
graphql-js.
一套比較完善的架構,眾所周知go的結構體對json非常友好,所以並不需要對資料有特殊的處理,還是很方便的。開啟終端輸入命令
go get github.com/graphql-go/graphql
Object
在服務端編程中,編寫的一切都可以稱之為對象(Object)。例如一個商品(goods)的執行個體可以有商品名(name)、價格(price)、購買連結(url)三個欄位。此時商品可以很自然的被稱為一個object,查詢的語句可以寫成:
{ goods{ name price url }}
如果此時我們要查詢商品和文章兩種object的資訊:
/* query 可以省去 */query{ goods{ name } article{ name }}
是否你已經發覺,query像一個大的object,它有goods和article兩個欄位。除此之外,mutation也是如此:
mutation{ addGoods(input:goodsInput){ name }}
這裡的addGoods可以看做是一個可以處理參數的對象,也就是某種意義上的函數。
總之,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 apiimport "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 grapheneclass 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還年輕,其他它越來越好!