知海

Go:graphql-relay-go(支持 Relay 的辅助库)

graphql-github-io-zh-Hans生态项目与库

GraphQL Relay for Go

graphql-relay-go 是 Go 语言生态中用于构建支持 React Relay 的 GraphQL 服务器的官方辅助库。它基于 graphql-go/graphql 实现,提供了完整的 Relay 服务端规范支持,帮助开发者快速构建符合 Relay 客户端要求的 GraphQL API。

主要特性

该库为 graphql-go 服务器补充了以下 Relay 核心能力:

  • Node 接口与全局对象标识:提供 Node 接口定义和全局 ID 的编解码工具(MarshalID / UnmarshalID),让客户端能够通过全局 ID 唯一标识和获取任意对象。
  • Connection 连接与分页:内置标准的 Connection 参数(ConnectionArgs)和连接定义工具(ConnectionDefinitions),轻松实现基于游标(cursor)的分页查询。
  • Mutation 变更规范:提供 Mutation 辅助类型,帮助构建符合 Relay 输入输出约定的变更操作。
  • graphql-go 无缝集成:完全基于 graphql-go 的类型系统构建,对现有项目侵入性低。

安装

使用标准的 go get 命令安装:

bash 复制代码
go get github.com/graphql-go/relay

该库依赖 graphql-go/graphql,安装时会自动拉取。

快速开始

以下示例展示了如何使用 graphql-relay-go 构建一个支持 Relay 的简单服务器。

go 复制代码
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"github.com/graphql-go/graphql"
	"github.com/graphql-go/relay"
)

// 定义一个简单的数据模型
type User struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

var users = map[string]User{
	"1": {ID: "1", Name: "Alice"},
	"2": {ID: "2", Name: "Bob"},
}

// 定义 Node 接口
var nodeDefinitions = relay.NewNodeDefinitions(relay.NodeDefinitionsConfig{
	ResolveNode: func(id string, info graphql.ResolveInfo) (interface{}, error) {
		return users[id], nil
	},
	ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
		if _, ok := p.Value.(User); ok {
			return userType
		}
		return nil
	},
})

// 定义 User 对象类型
var userType = graphql.NewObject(graphql.ObjectConfig{
	Name: "User",
	Interfaces: []*graphql.Interface{
		nodeDefinitions.NodeInterface,
	},
	Fields: graphql.Fields{
		"id":   relay.GlobalIDField("User", nil),
		"name": &graphql.Field{Type: graphql.String},
	},
})

// 构建查询类型
var queryType = graphql.NewObject(graphql.ObjectConfig{
	Name: "Query",
	Fields: graphql.Fields{
		"node": nodeDefinitions.NodeField,
		"users": &graphql.Field{
			Type:    graphql.NewList(userType),
			Resolve: func(p graphql.ResolveParams) (interface{}, error) {
				result := make([]User, 0, len(users))
				for _, u := range users {
					result = append(result, u)
				}
				return result, nil
			},
		},
	},
})

// 创建 Schema
var schema, _ = graphql.NewSchema(graphql.SchemaConfig{
	Query: queryType,
})

func main() {
	http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request) {
		var params struct {
			Query string `json:"query"`
		}
		if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

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

		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(result)
	})

	fmt.Println("GraphQL server running on :8080/graphql")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

在上述示例中:

  • relay.NewNodeDefinitions 注册了 Node 接口,用于将全局 ID 解析为对应的数据对象。
  • relay.GlobalIDField 自动将内部 ID 编码为 Relay 规定的全局 ID 格式。
  • nodeDefinitions.NodeField 暴露了 node 查询入口,客户端可通过该入口获取任意对象。

核心功能说明

全局 ID 编解码

Relay 要求所有对象通过全局 ID 引用。graphql-relay-go 提供了以下工具:

  • relay.MarshalID(typeName, id):将类型名与内部 ID 编码为全局 ID。
  • relay.UnmarshalID(globalID):将全局 ID 解码回类型名与内部 ID。
  • relay.GlobalIDField(typeName, resolve):快速定义对象类型中的 id 字段。

连接与分页

对于列表类字段,Relay 规范推荐使用 Connection 类型实现基于游标的分页:

go 复制代码
var userConnection = relay.ConnectionDefinitions(relay.ConnectionConfig{
	Name:     "User",
	NodeType: userType,
})

var queryFields = graphql.Fields{
	"users": &graphql.Field{
		Type:    userConnection.ConnectionType,
		Args:    relay.ConnectionArgs,
		Resolve: func(p graphql.ResolveParams) (interface{}, error) {
			// 从 p.Args 中读取 First, Last, Before, After 参数
			// 实现分页逻辑并返回 []User
			return nil, nil
		},
	},
}

relay.ConnectionArgs 提供了标准的分页参数(firstlastbeforeafter),ConnectionDefinitions 则自动生成 XxxConnectionXxxEdge 等类型。

Mutation 变更

Relay 的 Mutation 规范要求输入和输出都遵循特定结构。该库通过 relay.Mutation 和辅助函数简化了这一过程:

go 复制代码
var updateUserMutation = relay.Mutation(relay.MutationConfig{
	Name: "UpdateUser",
	InputFields: graphql.InputObjectConfigFieldMap{
		"id":   &graphql.InputObjectFieldConfig{Type: graphql.NewNonNull(graphql.String)},
		"name": &graphql.InputObjectFieldConfig{Type: graphql.String},
	},
	OutputFields: graphql.Fields{
		"user": &graphql.Field{Type: userType},
	},
	MutateAndGetPayload: func(p relay.MutationPayload) (interface{}, error) {
		// p.Input 包含输入参数
		// p.Context 包含请求上下文
		return nil, nil
	},
})

相关资源

总结

graphql-relay-go 是连接 Go 后端与 Relay 前端的重要桥梁。它忠实实现了 Relay 服务端规范,将全局 ID、游标分页、规范化变更等复杂协议细节封装为简洁的 API,使 Go 开发者能够专注于业务逻辑,快速构建出可被 Relay 客户端直接消费的 GraphQL 服务。对于使用 Relay 作为前端数据层、同时倾向 Go 作为后端语言的团队,这是一个值得优先考虑的官方方案。

帮助我们改进文档

发现翻译问题或内容错误?请告诉我们。