转到GraphQL API(CRUD)

图片


大家好! 在Habr上有很多关于GraphQL的文章,但是在浏览了它们之后,我发现它们都绕过了Go这样出色的语言。 今天,我将尝试纠正这种误解。 为此,我们将使用GraphQL在Go上编写一个API。


简而言之:GraphQL是用于构建API的查询语言,该API描述了以哪种形式请求和返回数据(有关官方资源graphql.imtqy.comHub的更多详细信息)


您可以认为GraphQL或REST更好


我们将提供经典的API:CRUD(创建,读取,更新,删除)在在线商店中添加,接收,编辑和删除产品。
在服务器端,我们将使用GraphQL的现成实现graphql-go


首先,您需要下载graphql-go,这可以通过以下命令完成


go get github.com/graphql-go/graphql 

接下来,我们描述产品的结构(以简化形式)


 type Product struct { ID int64 `json:"id"` Name string `json:"name"` Info string `json:"info,omitempty"` Price float64 `json:"price"` } 

ID唯一标识符, Name -名称, Info -产品信息, Price -价格


首先要做的是调用Do方法,该方法接受数据方案并将查询参数作为输入参数。 它将把结果数据返回给我们(以进一步传输给客户)


 result := graphql.Do(graphql.Params{ Schema: schema, RequestString: query, }) 

完整代码
 func executeQuery(query string, schema graphql.Schema) *graphql.Result { result := graphql.Do(graphql.Params{ Schema: schema, RequestString: query, }) if len(result.Errors) > 0 { fmt.Printf("errors: %v", result.Errors) } return result } func main() { http.HandleFunc("/product", func(w http.ResponseWriter, r *http.Request) { result := executeQuery(r.URL.Query().Get("query"), schema) json.NewEncoder(w).Encode(result) }) http.ListenAndServe(":8080", nil) } 

Schema -数据方案, RequestString查询字符串参数的值,在本例中为query


架构图


模式接受两种根数据类型: Query -不可变数据, Mutation -可变数据


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

询问


Query用于读取(并且仅读取)数据。 使用Query我们指定服务器应返回什么数据。
我们将编写Query数据类型的实现,在本例中,它将包含具有有关单个产品(产品)和商品列表(列表)的信息的字段


 var queryType = graphql.NewObject( graphql.ObjectConfig{ Name: "Query", Fields: graphql.Fields{ /*    ID http://localhost:8080/product?query={product(id:1){name,info,price}} */ "product": &graphql.Field{ Type: productType, Description: "Get product by id", //   ,    Args: graphql.FieldConfigArgument{ //       id "id": &graphql.ArgumentConfig{ Type: graphql.Int, }, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { id, ok := p.Args["id"].(int) if ok { //    ID for _, product := range products { if int(product.ID) == id { return product, nil } } } return nil, nil }, }, /*    http://localhost:8080/product?query={list{id,name,info,price}} */ "list": &graphql.Field{ Type: graphql.NewList(productType), Description: "Get product list", Resolve: func(params graphql.ResolveParams) (interface{}, error) { return products, nil }, }, }, }) 

queryType类型包含必需的NameFields以及可选的Description (用于文档)
反过来,“ Fields字段还包含必需的“ Type字段和可选的Args ,“ Resolve和“ Description字段


Args(参数)


参数-从客户端传输到服务器并影响返回数据结果的参数列表。 参数绑定到特定字段。 并且参数可以在QueryMutation传递。


 ?query={product(id:1){name,info,price}} 

在这种情况下, product字段的id参数值为1表示必须返回具有指定标识符的产品。
对于list省略list参数,但在实际应用中可以是例如limitoffset


解决(识别)


处理数据的所有逻辑(例如,数据库查询,处理和过滤)都在识别器中,由识别器返回数据,该数据将作为对请求的响应而传输到客户端。


型式


GraphQL使用其类型系统来描述数据。 您可以使用基本类型StringIntFloatBoolean和您自己的(自定义)类型。 对于我们的示例,我们将需要一个自定义Product类型,该类型将描述Product所有属性


 var productType = graphql.NewObject( graphql.ObjectConfig{ Name: "Product", Fields: graphql.Fields{ "id": &graphql.Field{ Type: graphql.Int, }, "name": &graphql.Field{ Type: graphql.String, }, "info": &graphql.Field{ Type: graphql.String, }, "price": &graphql.Field{ Type: graphql.Float, }, }, }, ) 

对于每个字段,都指定了基本类型,在这种情况下为graphql.Intgraphql.Stringgraphql.Float
嵌套字段的数量没有限制,因此您可以实现任何级别的图形系统。


变异


突变是这些可变数据,包括:添加,编辑和删除。 否则,变异与常规查询非常相似:它们也接受Args参数,并返回Resolve数据作为对查询的响应。


让我们为我们的产品编写变异。
  var mutationType = graphql.NewObject(graphql.ObjectConfig{ Name: "Mutation", Fields: graphql.Fields{ /*    http://localhost:8080/product?query=mutation+_{create(name:"Tequila",info:"Alcohol",price:99){id,name,info,price}} */ "create": &graphql.Field{ Type: productType, Description: "Create new product", Args: graphql.FieldConfigArgument{ "name": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.String), //     }, "info": &graphql.ArgumentConfig{ Type: graphql.String, //    }, "price": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.Float), }, }, Resolve: func(params graphql.ResolveParams) (interface{}, error) { rand.Seed(time.Now().UnixNano()) product := Product{ ID: int64(rand.Intn(100000)), //   ID Name: params.Args["name"].(string), Info: params.Args["info"].(string), Price: params.Args["price"].(float64), } products = append(products, product) return product, nil }, }, /*    id http://localhost:8080/product?query=mutation+_{update(id:1,price:195){id,name,info,price}} */ "update": &graphql.Field{ Type: productType, Description: "Update product by id", Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.Int), }, "name": &graphql.ArgumentConfig{ Type: graphql.String, }, "info": &graphql.ArgumentConfig{ Type: graphql.String, }, "price": &graphql.ArgumentConfig{ Type: graphql.Float, }, }, Resolve: func(params graphql.ResolveParams) (interface{}, error) { id, _ := params.Args["id"].(int) name, nameOk := params.Args["name"].(string) info, infoOk := params.Args["info"].(string) price, priceOk := params.Args["price"].(float64) product := Product{} for i, p := range products { //     if int64(id) == p.ID { if nameOk { products[i].Name = name } if infoOk { products[i].Info = info } if priceOk { products[i].Price = price } product = products[i] break } } return product, nil }, }, /*    id http://localhost:8080/product?query=mutation+_{delete(id:1){id,name,info,price}} */ "delete": &graphql.Field{ Type: productType, Description: "Delete product by id", Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.Int), }, }, Resolve: func(params graphql.ResolveParams) (interface{}, error) { id, _ := params.Args["id"].(int) product := Product{} for i, p := range products { if int64(id) == p.ID { product = products[i] //     products = append(products[:i], products[i+1:]...) } } return product, nil }, }, }, }) 

都类似于queryType 。 只有一个小的特征类型graphql.NewNonNull(graphql.Int) ,它告诉我们该字段不能为空(类似于MySQL中的NOT NULL


仅此而已。 现在,我们有一个简单的Go CRUD API用于处理商品。 在此示例中,我们没有使用数据库,而是研究了如何创建数据模型并使用突变对其进行操作。


例子


如果您通过下载了源


 go get github.com/graphql-go/graphql 

只需使用示例进入目录


 cd examples/crud 

并运行该应用程序


 go run main.go 

您可以使用以下查询:
通过ID获取产品
http://localhost:8080/product?query={product(id:1){name,info,price}}


获取产品列表
http://localhost:8080/product?query={list{id,name,info,price}}


添加新产品
http://localhost:8080/product?query=mutation+_{create(name:"Tequila",info:"Strong alcoholic beverage",price:999){id,name,info,price}}


产品编辑
http://localhost:8080/product?query=mutation+_{update(id:1,price:195){id,name,info,price}}


通过ID删除产品
http://localhost:8080/product?query=mutation+_{delete(id:1){id,name,info,price}}


如果使用REST,请注意GraphQL作为可能的替代方法。 是的,乍看之下似乎比较困难,但是值得一开始,几天之内您就会掌握这项技术。 至少它将是有用的。

Source: https://habr.com/ru/post/zh-CN418203/


All Articles