GraphQL提供了一种灵活而有效的方式来查询服务器中的数据。 它正在成为设计后端的流行技术,通常会替换或封装一些不灵活的REST API,并让客户负责决定他们需要的数据。 现在有许多用于编写JavaScript的GraphQL客户端和服务器的库和框架,其中最着名的是Apollo和Graphcool 。 Apollo团队还开发了针对WebSockets的GraphQL协议,该协议主要用于Apollo Client和Graphcool中的Subscriptions。

1.介绍graphqlws

今天,我们开放了一个新的Go库来填补这个空白: graphqlw。 它有一个简单的目的:

  1. 实现GraphQL在WebSocket(由所有流行的GraphQL客户端使用),所以不必考虑与net/http无缝集成
  2. 与net/http无缝集成
  3. 提供访问已建立订阅的简单方法,执行身份验证并向相应的客户端发送更新。 你可以在https://github.com/functionalfoundry/graphqlws上的GitHub上找到它。

第1步 - 基本设置

authToken
package main

import (
	"net/http"

	"github.com/functionalfoundry/graphqlws"
	"github.com/graphql-go/graphql"
)

func main() {
	// Create a GraphQL schema with a subscription object
	schema, err := graphql.NewSchema(graphql.SchemaConfig{
		Subscription: graphql.NewObject(...),
	})
	
	// Create a subscription manager
	subscriptionManager := graphqlws.NewSubscriptionManager(&schema)

	// Create a WebSocket/HTTP handler
	graphqlwsHandler := graphqlws.NewHandler(graphqlws.HandlerConfig{
		// Wire up the GraphqL WebSocket handler with the subscription manager
		SubscriptionManager: subscriptionManager,

		// Optional: Add a hook to resolve auth tokens into users that are
		// then stored on the GraphQL WS connections
		Authenticate: func(authToken string) (interface{}, error) {
			// This is just a dumb example
			return "Joe", nil
		},
	})

	// The handler integrates seamlessly with existing HTTP servers
	http.Handle("/subscriptions", graphqlwsHandler)
	http.ListenAndServe(":8080", nil)
}

第2步 - 使用 Subscriptions

Graphqlws库本身仅仅实现了基于WebSocket协议的GraphQL。 它没有实现任何开箱即用的订阅。 一个典型的服务器实现将监听数据库的变化,并通过识别哪些订阅需要更新,重新执行这些订阅的查询并将结果发送给相应的订阅客户端来对这些更改做出反应。

以下代码示例显示了订阅管理器在任何时候如何检索订阅。 他们按照他们的关系分组。 每个连接都有唯一的ID,可选地,还有前面描述的Authenticate函数返回的用户。

QueryVariablesOperationNameFieldsDocument
QueryVariablesOperationNameSendData
// This assumes you have access to the above subscription manager
subscription := subscriptionManager.Subscriptions()

for _, conn := range subscriptions {
	// Things you have access to here:
	conn.ID()   // The connection ID
	conn.User() // The user returned from the subscription manager's Authenticate
	
	for _, subscription := range subscriptions[conn] {
		// Things you have access to here:
		subscription.ID            // The subscription ID (unique per conn)
		subscription.OperationName // The name of the subcription
		subscription.Query         // The subscription query/queries string
		subscription.Variables     // The subscription variables
		subscription.Document      // The GraphQL AST for the subscription
		subscription.Fields        // The names of top-level queries
		subscription.Connection    // The GraphQL WS connection

		// Prepare an execution context for running the query
		ctx := context.Context()

		// Re-execute the subscription query
		params := graphql.Params{
			Schema:         schema, // The GraphQL schema
			RequestString:  subscription.Query,
			VariableValues: subscription.Variables,
			OperationName:  subscription.OperationName,
			Context:        ctx,
		}
		result := graphql.Do(params)

		// Send query results back to the subscriber at any point
		data := graphqlws.DataMessagePayload{
			// Data can be anything (interface{})
			Data:   result.Data, 
			// Errors is optional ([]error)
			Errors: graphqlws.ErrorsFromGraphQLErrors(result.Errors),
		}
		subscription.SendData(&data)
	}
}

第3步 - 与graphql-go / handler结合使用

net/http
package main

import (
	"net/http"
	
	"github.com/functionalfoundry/graphqlws"
	"github.com/graphql-go/graphql"
	"github.com/graphql-go/handler"
)

func main() {
	// Create a GraphQL schema with a subscription object
	schema, err := graphql.NewSchema(graphql.SchemaConfig{
		Subscription: graphql.NewObject(...),
	})
	
	// Create a GraphQL over WebSocket handler
	subscriptionManager := graphqlws.NewSubscriptionManager(&schema)
	graphqlwsHandler := graphqlws.NewHandler(graphqlws.HandlerConfig{
		SubscriptionManager: subscriptionManager,
	})
	
	// Create a GraphQL over HTTP handler
	graphqlHandler := handler.New(&handler.Config{
		Schema: &schema,
		Pretty: true,
		GraphiQL: true,
	})
	
	// Serve both handlers on different endpoints
	http.Handle("/", graphqlHandler)
	http.Handle("/subscriptions", graphqlwsHandler)
	http.ListenAndServe(":8080", nil)
}
概要
graphqlws

在GitHub上提交 bug 或 issue 。

如果您正在寻找下一个项目的React或GraphQL开发方面的帮助,我们很乐意在Functional Foundry处听到您的消息。