README
¶
Note: We're looking for a new maintainer for graphqlws
. Please reach out via jannis@thegraph.com if you're interested.
graphqlws
Implementation of the GraphQL over WebSocket protocol in Go. Brought to you by Functional Foundry.
Getting started
- Install dependencies:
go get github.com/sirupsen/logrus go get github.com/x-cray/logrus-prefixed-formatter go get github.com/google/uuid go get github.com/gorilla/websocket go get github.com/graphql-go/graphql
- Clone the repository:
mkdir -p "$GOPATH/src/github.com/functionalfoundry" cd "$GOPATH/src/github.com/functionalfoundry" git clone https://github.com/functionalfoundry/graphqlws
- Run the tests:
cd graphqlws go test
- Run the example server:
go run graphqlws/examples/server
Usage
Setup
package main
import (
"net/http"
"github.com/functionalfoundry/graphqlws"
"github.com/graphql-go/graphql"
)
func main() {
// Create a GraphQL schema
schema, err := graphql.NewSchema(...)
// 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)
}
Working with subscriptions
// This assumes you have access to the above subscription manager
subscriptions := subscriptionManager.Subscriptions()
for conn, _ := range subscriptions {
// Things you have access to here:
conn.ID() // The connection ID
conn.User() // The user returned from the Authenticate function
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 operation
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)
}
}
Logging
graphqlws
uses logrus for logging.
In the future we might remove those logs entirely to leave logging entirely to developers
using graphqlws
. Given the current solution, you can control the logging level of
graphqlws
by setting it through logrus
:
import (
log "github.com/sirupsen/logrus"
)
...
log.SetLevel(log.WarnLevel)
License
Copyright © 2017-2019 Functional Foundry, LLC.
Licensed under the MIT License.
Documentation
¶
Index ¶
- func ErrorsFromGraphQLErrors(errors []gqlerrors.FormattedError) []error
- func NewHandler(config HandlerConfig) http.Handler
- func NewLogger(prefix string) *log.Entry
- type AuthenticateFunc
- type Connection
- type ConnectionConfig
- type ConnectionEventHandlers
- type ConnectionSubscriptions
- type DataMessagePayload
- type HandlerConfig
- type InitMessagePayload
- type OperationMessage
- type StartMessagePayload
- type Subscription
- type SubscriptionManager
- type SubscriptionSendDataFunc
- type Subscriptions
Constants ¶
Variables ¶
Functions ¶
func ErrorsFromGraphQLErrors ¶
func ErrorsFromGraphQLErrors(errors []gqlerrors.FormattedError) []error
ErrorsFromGraphQLErrors convert from GraphQL errors to regular errors.
func NewHandler ¶
func NewHandler(config HandlerConfig) http.Handler
NewHandler creates a WebSocket handler for GraphQL WebSocket connections. This handler takes a SubscriptionManager and adds/removes subscriptions as they are started/stopped by the client.
Types ¶
type AuthenticateFunc ¶
AuthenticateFunc is a function that resolves an auth token into a user (or returns an error if that isn't possible).
type Connection ¶
type Connection interface { // ID returns the unique ID of the connection. ID() string // User returns the user associated with the connection (or nil). User() interface{} // SendData sends results of executing an operation (typically a // subscription) to the client. SendData(string, *DataMessagePayload) // SendError sends an error to the client. SendError(error) }
Connection is an interface to represent GraphQL WebSocket connections. Each connection is associated with an ID that is unique to the server.
func NewConnection ¶
func NewConnection(ws *websocket.Conn, config ConnectionConfig) Connection
NewConnection establishes a GraphQL WebSocket connection. It implements the GraphQL WebSocket protocol by managing its internal state and handling the client-server communication.
type ConnectionConfig ¶
type ConnectionConfig struct { Authenticate AuthenticateFunc EventHandlers ConnectionEventHandlers }
ConnectionConfig defines the configuration parameters of a GraphQL WebSocket connection.
type ConnectionEventHandlers ¶
type ConnectionEventHandlers struct { // Close is called whenever the connection is closed, regardless of // whether this happens because of an error or a deliberate termination // by the client. Close func(Connection) // StartOperation is called whenever the client demands that a GraphQL // operation be started (typically a subscription). Event handlers // are expected to take the necessary steps to register the operation // and send data back to the client with the results eventually. StartOperation func(Connection, string, *StartMessagePayload) []error // StopOperation is called whenever the client stops a previously // started GraphQL operation (typically a subscription). Event handlers // are expected to unregister the operation and stop sending result // data to the client. StopOperation func(Connection, string) }
ConnectionEventHandlers define the event handlers for a connection. Event handlers allow other system components to react to events such as the connection closing or an operation being started or stopped.
type ConnectionSubscriptions ¶
type ConnectionSubscriptions map[string]*Subscription
ConnectionSubscriptions defines a map of all subscriptions of a connection by their IDs.
type DataMessagePayload ¶
type DataMessagePayload struct { Data interface{} `json:"data"` Errors []error `json:"errors"` }
DataMessagePayload defines the result data of an operation.
type HandlerConfig ¶
type HandlerConfig struct { SubscriptionManager SubscriptionManager Authenticate AuthenticateFunc }
HandlerConfig stores the configuration of a GraphQL WebSocket handler.
type InitMessagePayload ¶
type InitMessagePayload struct {
AuthToken string `json:"authToken"`
}
InitMessagePayload defines the parameters of a connection init message.
type OperationMessage ¶
type OperationMessage struct { ID string `json:"id"` Type string `json:"type"` Payload interface{} `json:"payload"` }
OperationMessage represents a GraphQL WebSocket message.
func (OperationMessage) String ¶
func (msg OperationMessage) String() string
type StartMessagePayload ¶
type StartMessagePayload struct { Query string `json:"query"` Variables map[string]interface{} `json:"variables"` OperationName string `json:"operationName"` }
StartMessagePayload defines the parameters of an operation that a client requests to be started.
type Subscription ¶
type Subscription struct { ID string Query string Variables map[string]interface{} OperationName string Document *ast.Document Fields []string Connection Connection SendData SubscriptionSendDataFunc }
Subscription holds all information about a GraphQL subscription made by a client, including a function to send data back to the client when there are updates to the subscription query result.
func (*Subscription) MatchesField ¶
func (s *Subscription) MatchesField(field string) bool
MatchesField returns true if the subscription is for data that belongs to the given field.
type SubscriptionManager ¶
type SubscriptionManager interface { // Subscriptions returns all registered subscriptions, grouped // by connection. Subscriptions() Subscriptions // AddSubscription adds a new subscription to the manager. AddSubscription(Connection, *Subscription) []error // RemoveSubscription removes a subscription from the manager. RemoveSubscription(Connection, *Subscription) // RemoveSubscriptions removes all subscriptions of a client connection. RemoveSubscriptions(Connection) }
SubscriptionManager provides a high-level interface to managing and accessing the subscriptions made by GraphQL WS clients.
func NewSubscriptionManager ¶
func NewSubscriptionManager(schema *graphql.Schema) SubscriptionManager
NewSubscriptionManager creates a new subscription manager.
func NewSubscriptionManagerWithLogger ¶
func NewSubscriptionManagerWithLogger(schema *graphql.Schema, logger *log.Entry) SubscriptionManager
type SubscriptionSendDataFunc ¶
type SubscriptionSendDataFunc func(*DataMessagePayload)
SubscriptionSendDataFunc is a function that sends updated data for a specific subscription to the corresponding subscriber.
type Subscriptions ¶
type Subscriptions map[Connection]ConnectionSubscriptions
Subscriptions defines a map of connections to a map of subscription IDs to subscriptions.
Directories
¶
Path | Synopsis |
---|---|
examples
|
|