Github提供的GraphQL接口非常全面刑峡,那么我們該如何搭建出自己的接口呢斜纪?好在GraphQL提供了很多語言的解決方案乳蓄。本文主要闡述如何用go搭建自己的GraphQL服務(wù)器。如果了解GraphQL建議先閱讀GraphQL — API查詢語言 或相關(guān)資料褂微。
graphql-go
An implementation of GraphQL in Go. Follows the official reference implementation
graphql-js
.
一套比較完善的框架友题,眾所周知go的結(jié)構(gòu)體對json非常友好嗤堰,所以并不需要對數(shù)據(jù)有特殊的處理,還是很方便的度宦。打開終端輸入命令
go get github.com/graphql-go/graphql
Object
在服務(wù)端編程中踢匣,編寫的一切都可以稱之為對象(Object)。例如一個商品(goods)的實例可以有商品名(name)戈抄、價格(price)离唬、購買鏈接(url)三個字段。此時商品可以很自然的被稱為一個object划鸽,查詢的語句可以寫成:
{
goods{
name
price
url
}
}
如果此時我們要查詢商品和文章兩種object的信息:
/* query 可以省去 */
query{
goods{
name
}
article{
name
}
}
是否你已經(jīng)發(fā)覺输莺,query像一個大的object戚哎,它有g(shù)oods和article兩個字段。除此之外嫂用,mutation也是如此:
mutation{
addGoods(input:goodsInput){
name
}
}
這里的addGoods
可以看做是一個可以處理參數(shù)的對象型凳,也就是某種意義上的函數(shù)。
總之尸折,GraphQL服務(wù)端的編程就是一個又一個的對象將形成的嵌套結(jié)構(gòu)(schema)組織起來啰脚,并對外提供服務(wù)。
query&mutation
為了防止低級錯誤的發(fā)生实夹,在當(dāng)前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)
注意:數(shù)組相當(dāng)于新的object類型粒梦。
定義query object
var queryType = graphql.NewObject(
graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
// 無需處理參數(shù)
"goodsList": &graphql.Field{
Type:goodsListType,
// 處理結(jié)構(gòu)體的回調(diào)函數(shù)亮航,直接返回處理完成的結(jié)構(gòu)體即可
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return result, err
},
},
// 參數(shù)是id
"goods": &graphql.Field{
Type: goodsType,
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
Type: graphql.String,
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
// 獲取參數(shù)
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),
}
// 處理數(shù)據(jù)
return result,err
},
},
},
},
)
然而匀们,input類型并不能直接轉(zhuǎn)換為struct缴淋,而是一個map[string]interface{}類型,還需要進行手動轉(zhuǎn)換泄朴。
定義schema
var schema, _ = graphql.NewSchema(
graphql.SchemaConfig{
Query: queryType,
Mutation: mutationType,
},
)
至此重抖,我們的全部的object定義完成。
提供服務(wù)
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界面吧局扶!
結(jié)束語
如果你覺得這樣的代碼談不上優(yōu)雅恨统,甚至非常丑陋,那就對了三妈。因為我也這樣覺得畜埋,看一看隔壁python的實現(xiàn)方式:
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本身反射系統(tǒng)并不夠完善畴蒲,沒有python各種各樣的魔術(shù)方法悠鞍,沒有泛型,或者說go本身不太適合編寫框架類的代碼模燥。在編寫的過程中咖祭,冗余非常多,當(dāng)然也可能是框架本身的問題
不可否認(rèn)的是涧窒,go確實是非常不錯的一門語言心肪,雖然開發(fā)效率無法與python媲美,但是在多并發(fā)環(huán)境下纠吴,go表現(xiàn)出非常出色硬鞍,同時擁有與C級別的運行速度和豐富的生態(tài)。
go還年輕,其他它越來越好固该!