我要写一下最近流行的GraphQL
这篇文章是2017年go Advent Calendar的第15天的文章。
介绍
最近有很多新技术出现,例如grpc和GraphQL。您写api的时候,通常会使用什么作为api的接口呢?一般来说,返回json格式的响应很常见。
我想要解释一下本次的主题,即关于GraphQL的概述和在Go语言中的实现。
GraphQL的概述
GraphQL是由Facebook开发的一种查询语言,于2015年在React.js Conf上进行了发布。
虽然在国内并不像其他地方那样流行,但由于GitHub的采用以及一些公司的采用,它的知名度正在不断提升。
GraphQL的主要特点有两个:客户端可以指定所需信息,一次请求可以获取多个(可选的)资源。
我们可以尝试使用Github api Explorer来测试一下使用GitHub API获取信息的代码。
{
viewer {
login,
avatarUrl,
company,
name,
url
}
}
在点击链接后,当您写入此类查询时,将返回如下响应。
{
"data": {
"viewer": {
"login": "takochuu",
"avatarUrl": "https://avatars1.githubusercontent.com/u/207675?v=4",
"company": "Japan",
"name": "",
"url": "https://github.com/takochuu"
}
}
}
嗯?很简单吧。
这次,我将使用go语言来创建一个使用GraphQL的API服务器的源代码。
使用GraphQL实现服务器
我们现在要开始实施了。作为介绍,我认为最好让大家进行这个教程。
我打算使用github.com/graphql-go/graphql作为本次的GraphQL库。虽然有点担心它已经有一个月没有提交过新的代码了,但目前在go语言中处理GraphQL时,它是star数最多的库,所以还是选择了它。
查询(读取)
首先,我们将尝试使用GraphQL来实现服务器端的读取处理。
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"github.com/graphql-go/graphql"
)
var q graphql.ObjectConfig = graphql.ObjectConfig{
Name: "query",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: graphql.ID,
Resolve: resolveID,
},
"name": &graphql.Field{
Type: graphql.String,
Resolve: resolveName,
},
},
}
var schemaConfig graphql.SchemaConfig = graphql.SchemaConfig{
Query: graphql.NewObject(q),
}
var schema, _ = graphql.NewSchema(schemaConfig)
func executeQuery(query string, schema graphql.Schema) *graphql.Result {
r := graphql.Do(graphql.Params{
Schema: schema,
RequestString: query,
})
if len(r.Errors) > 0 {
fmt.Printf("エラーがあるよ: %v", r.Errors)
}
j, _ := json.Marshal(r)
fmt.Printf("%s \n", j)
return r
}
func handler(w http.ResponseWriter, r *http.Request) {
bufBody := new(bytes.Buffer)
bufBody.ReadFrom(r.Body)
query := bufBody.String()
result := executeQuery(query, schema)
json.NewEncoder(w).Encode(result)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
func resolveID(p graphql.ResolveParams) (interface{}, error) {
return 1, nil
}
func resolveName(p graphql.ResolveParams) (interface{}, error) {
return "hoge", nil
}
在Go中实现GraphQL的服务器端实现中,我们可以通过定义schema并将其与查询一起传递给graphql.Do来进行处理。
我们可以通过运行go run main.go启动服务器,并使用curl来获取数据。
#!/bin/bash
curl -X POST -d '{ id }' http://localhost:8080/
curl -X POST -d '{ name }' http://localhost:8080/
curl -X POST -d '{ id, name }' http://localhost:8080/
从服务器返回的响应在这里。
{"data":{"id":"1"}}
{"data":{"name":"hoge"}}
{"data":{"id":"1","name":"hoge"}}
通过上述的curl命令,可以看到返回了与指定字段相对应的值。
"id": &graphql.Field{
Type: graphql.ID,
Resolve: resolveID,
},
},
对于指定的字段,value的生成与Resolve中定义的resolveID和resolveName相关,并返回该值。
此外,您还可以将参数传递给查询。
只需对之前的服务器端代码进行一些修改,如下所示。
"id": &graphql.Field{
Type: graphql.ID,
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
Type: graphql.Int,
},
},
Resolve: resolveID,
},
func resolveID(p graphql.ResolveParams) (interface{}, error) {
return p.Args["id"], nil
}
在这种情况下,更改之前运行的curl命令,并发送这样的查询。
curl -X POST -d '{ id(id: 100), name }' http://localhost:8080/
根据响应,可以看到将参数传递为100时,它会作为id的值返回。
{"data":{"id":"100","name":"hoge"}}
虽然这次简化了,但在具体实施服务时,将使用传递给Resolve映射函数的参数来提取数据。
突变 (tū
在GraphQL中,读取数据时使用query操作,而在执行写操作(例如写入)时使用mutation操作。之前提到的源代码是使用query操作进行数据获取处理的,接下来我们将介绍使用mutation操作进行服务器实现的方法。
var m graphql.ObjectConfig = graphql.ObjectConfig{
Name: "User",
Fields: graphql.Fields{
"user": &graphql.Field{
Type: graphql.NewObject(graphql.ObjectConfig{
Name: "Params",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: graphql.Int,
},
"address": &graphql.Field{
Type: graphql.NewObject(graphql.ObjectConfig{
Name: "state",
Fields: graphql.Fields{
"state": &graphql.Field{
Type: graphql.String,
},
"city": &graphql.Field{
Type: graphql.String,
},
},
}),
},
},
}),
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
Type: graphql.Int,
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
// ここで更新処理をする
return User{
Id: 10000,
Address: Address{
State: "三宿",
City: "世田谷区",
},
}, nil
},
},
},
}
type User struct {
Id int64 `json:"id"`
Address Address `json:"address"`
}
type Address struct {
State string `json:"state"`
City string `json:"city"`
}
var schemaConfig graphql.SchemaConfig = graphql.SchemaConfig{
Query: graphql.NewObject(q),
Mutation: graphql.NewObject(m),
}
先前的源代码进行修改,为graphql.SchemaConfig定义Mutation字段。
然后,同样地定义graphql.ObjectConfig。
我们可以像上述源代码那样定义结构体,并使用标签进行映射。
现在就来尝试查询吧。
- リクエスト
curl -X POST -d 'mutation { user(id: 100){ id, address{ state, city }}}' http://localhost:8080/
- レスポンス
{"data":{"user":{"address":{"city":"世田谷区","state":"三宿"},"id":10000}}}
最后
由於我很少在國內聽到GraphQL受到熱烈討論的話題,所以我希望國內能增加更多引入GraphQL的案例。
這只是簡單地介紹GraphQL,就到這裡吧。請期待明天的內容!