为API而生（而不是为数据库而生）的查询语言，GraphQL不是一个和传统SQL一样的查询语言，它是位于API之前的一层抽象，且并不依赖于任何特定的数据或存储引擎。REST和GraphQL的区别首先，让我们看看RESTful方法和GraphQL方法的区别。想象下我们正在构建一个能返回本站所有教程的服务，如果我们需要某些指定的教程信息，通常来说，我们会创建一个API端点以允许我们以一个ID来检索指定的教程。# A dummy endpoint that takes in an ID path parameter 'http://api.tutorialedge.net/tutorial/:id'如果给定的是一个合法的 ID ，它将会返回一个响应体，该响应体可能会如下所示：{ "title": "Go GraphQL Tutorial", "Author": "Elliot Forbes", "slug": "/golang/go-graphql-beginners-tutorial/", "views": 1, "key" : "value" }现在，假设我们想显示一个列表，列出指定的作者撰写的前5个帖子。我们可以使用/author/:id这样的API来检索出所有由该作者撰写的帖子，然后再执行后续的调用获取排名前5的帖子。亦或者，我们可以创建一个新的API来返回这些数据。上述的解决方案听起来并没有什么特别之处，因为它们创建了大量无用的请求或者返回了过多的冗余信息，这也暴露了RESTful方法的一些缺陷。此时，就轮到 GraphQL 入场了。通过 GraphQL ，我们可以在查询中精确定义我们想要返回的数据。因此，如果我们需要上述的教程信息，我们可以创建一个查询，如下所示：{ tutorial(id: 1) { id title author { name tutorials } comments { body } } }随后，它就会返回该教程的作者信息以及指定id教程下该作者所撰写的其他教程列表。这些数据都是我们所需的，但却不用通过发送额外的REST请求来获得快速开始创建一个包含以下内容的文件：main.gopackage main import ( "log" "net/http" graphql "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/relay" ) type query struct{} func (_ *query) Hello() string { return "Hello, world!" } func main() { s := ` type Query { hello: String! } ` schema := graphql.MustParseSchema(s, &query{}) http.Handle("/query", &relay.Handler{Schema: schema}) log.Fatal(http.ListenAndServe(":8080", nil)) }使用go运行该程序go run main.go使用curl测试查看结果curl -XPOST -d '{"query": "{ hello }"}' localhost:8080/query将会输出{"data":{"hello":"Hello, world!"}}一般流程1. 定义schemaconst schema string = ` scalar Long schema { query: Query } # 以太坊区块 type Block { number: Long! hash: String! nonce: Long! timestamp: Long! difficulty: Long! } type Query { block(number: Long, hash: String): Block } `参考官方入门文档2. 自定义类型解码接口实现（如果有）type Long uint64 // ImplementsGraphQLType returns true if Long implements the provided GraphQL type. func (b Long) ImplementsGraphQLType(name string) bool { return name == "Long" } // UnmarshalGraphQL unmarshals the provided GraphQL query data. func (b *Long) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { case string: // uncomment to support hex values //if strings.HasPrefix(input, "0x") { // // apply leniency and support hex representations of longs. // value, err := hexutil.DecodeUint64(input) // *b = Long(value) // return err //} else { value, err := strconv.ParseUint(input, 10, 64) *b = Long(value) return err //} case int32: *b = Long(input) case uint32: *b = Long(input) case uint64: *b = Long(input) default: err = fmt.Errorf("unexpected type %T for Long", input) } return err }3. 定义解析器type Block struct { ... } func (b *Block) Number(ctx context.Context) (Long, error) { ... } func (b *Block) Hash(ctx context.Context) (string, error) { ... } func (b *Block) Difficulty(ctx context.Context) (Long, error) { ... } func (b *Block) Timestamp(ctx context.Context) (Long, error) { ... } func (b *Block) Nonce(ctx context.Context) (Long, error) { ... } type Resolver struct{} func (r *Resolver) Block(ctx context.Context, args struct { Number *Long Hash *string }) (*Block, error) { ... }如果解析的字段没有错误可以省去error返回值func (b *Block) Number(ctx context.Context) Long { ... }4. 组合视图schema和解析器resolver并启动http服务使用包（github.com/graph-gophers/graphql-go）自带的包装器处理前端请求type Handler struct { Schema *graphql.Schema } func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { var params struct { Query string `json:"query"` OperationName string `json:"operationName"` Variables map[string]interface{} `json:"variables"` } if err := json.NewDecoder(r.Body).Decode(&params); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } response := h.Schema.Exec(r.Context(), params.Query, params.OperationName, params.Variables) responseJSON, err := json.Marshal(response) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(responseJSON) }func main() { handler := &relay.Handler{Schema: graphql.MustParseSchema(schema, &Resolver{})} http.Handle("/graphql", handler) log.Fatal(http.ListenAndServe(":8080", nil)) }网页查询IDE可以利用GraphiQL实现网页查询测试结果，先定义http处理器type GraphiQL struct{} func respond(w http.ResponseWriter, body []byte, code int) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("X-Content-Type-Options", "nosniff") w.WriteHeader(code) _, _ = w.Write(body) } func errorJSON(msg string) []byte { buf := bytes.Buffer{} fmt.Fprintf(&buf, `{"error": "%s"}`, msg) return buf.Bytes() } func (h GraphiQL) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" { respond(w, errorJSON("only GET requests are supported"), http.StatusMethodNotAllowed) return } w.Header().Set("Content-Type", "text/html") w.Write(graphiql) } var graphiql = []byte(` Loading... `)然后绑定路由即可http.Handle("/graphql/ui", GraphiQL{})