Documentation ¶
Overview ¶
Package gws implements a client and server for the GraphQL over Websocket protocol.
Index ¶
- Constants
- Variables
- func NewHandler(h Handler, opts ...ServerOption) http.Handler
- type Client
- type CompressionMode
- type Conn
- type ConnOption
- type ConnectParams
- type DialOption
- type ErrIO
- type ErrUnexpectedMessage
- type ErrUnsupportedMsgType
- type Handler
- type HandlerFunc
- type MessageType
- type Request
- type Response
- type ServerError
- type ServerOption
- type Stream
- type Subscription
Examples ¶
Constants ¶
const ( MessageText = MessageType(websocket.MessageText) MessageBinary = MessageType(websocket.MessageBinary) )
Re-export message type provided by the underlying Websocket package.
Variables ¶
var DefaultConnectParams = ConnectParams{ Backoff: backoff.DefaultConfig, MinConnectTimeout: 20 * time.Second, }
DefaultConnectParams is a default configuration for retrying with a backoff.
var ErrStreamClosed = errors.New("gws: stream is closed")
ErrStreamClosed is returned by Send if the stream is closed before Send is called.
var ErrUnsubscribed = errors.New("gws: received cancelled due to unsubscribe")
ErrUnsubscribed is returned by a subscription receive when the subscription is unsubscribed to or completed before the next response is received.
Functions ¶
func NewHandler ¶
func NewHandler(h Handler, opts ...ServerOption) http.Handler
NewHandler configures an http.Handler, which will upgrade incoming connections to WebSocket and serve the "graphql-ws" subprotocol.
Example ¶
h := func(s *Stream, req *Request) error { // Remember to always close the stream when done sending. defer s.Close() // Use your choice of a GraphQL runtime to execute the query // Then, return the results JSON encoded with a *Response. r := &Response{ Data: []byte(`{"hello":{"world":"this is example data"}}`), } return s.Send(context.TODO(), r) } // Simply register the handler with a http mux of your choice // and it will handle the rest. // http.Handle("graphql", NewHandler(HandlerFunc(h))) err := http.ListenAndServe(":80", nil) if err != nil { // Always handle your errors return }
Output:
Types ¶
type Client ¶
type Client interface { // Query provides an RPC like API for performing GraphQL queries. Query(context.Context, *Request) (*Response, error) // Subscribe provides an RPC like API for performing GraphQL subscription queries. Subscribe(context.Context, *Request) (*Subscription, error) }
Client provides high-level API for making GraphQL requests over WebSocket.
func NewClient ¶
NewClient takes a connection and initializes a client over it.
Example (Concurrent) ¶
conn, err := Dial(context.TODO(), "ws://example.com") if err != nil { // Make sure to handle the error return } defer conn.Close() // Performing queries is completely concurrent safe. client := NewClient(conn) respCh := make(chan *Response) go func() { resp, err := client.Query(context.TODO(), &Request{Query: "{ hello { world } }"}) if err != nil { // Remember, always handle errors return } respCh <- resp }() go func() { resp, err := client.Query(context.TODO(), &Request{Query: "{ hello { world } }"}) if err != nil { // Remember, always handle errors return } respCh <- resp }() for resp := range respCh { // Always check resp.Errors fmt.Println(resp) }
Output:
Example (Query) ¶
conn, err := Dial(context.TODO(), "ws://example.com") if err != nil { // Make sure to handle the error return } defer conn.Close() client := NewClient(conn) resp, err := client.Query(context.TODO(), &Request{Query: "{ hello { world } }"}) if err != nil { // Remember, always handle errors return } // Always check resp.Errors var exampleResp struct { Hello struct { World string `json:"world"` } `json:"hello"` } err = json.Unmarshal(resp.Data, &exampleResp) if err != nil { return } // Now, exampleResp.Hello.World would be your query result.
Output:
type CompressionMode ¶
type CompressionMode websocket.CompressionMode
CompressionMode represents the modes available to the deflate extension. See https://tools.ietf.org/html/rfc7692
A compatibility layer is implemented for the older deflate-frame extension used by safari. See https://tools.ietf.org/html/draft-tyoshino-hybi-websocket-perframe-deflate-06 It will work the same in every way except that we cannot signal to the peer we want to use no context takeover on our side, we can only signal that they should. It is however currently disabled due to Safari bugs. See https://github.com/nhooyr/websocket/issues/218
const ( // CompressionNoContextTakeover grabs a new flate.Reader and flate.Writer as needed // for every message. This applies to both server and client side. // // This means less efficient compression as the sliding window from previous messages // will not be used but the memory overhead will be lower if the connections // are long lived and seldom used. // // The message will only be compressed if greater than 512 bytes. // CompressionNoContextTakeover CompressionMode = iota // CompressionContextTakeover uses a flate.Reader and flate.Writer per connection. // This enables reusing the sliding window from previous messages. // As most WebSocket protocols are repetitive, this can be very efficient. // It carries an overhead of 8 kB for every connection compared to CompressionNoContextTakeover. // // If the peer negotiates NoContextTakeover on the client or server side, it will be // used instead as this is required by the RFC. // CompressionContextTakeover // CompressionDisabled disables the deflate extension. // // Use this if you are using a predominantly binary protocol with very // little duplication in between messages or CPU and memory are more // important than bandwidth. // CompressionDisabled )
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn is a client connection that should be closed by the client.
func Dial ¶
Dial creates a connection to the given endpoint. By default, it's a non-blocking dial (the function won't wait for connections to be established, and connecting happens in the background).
Example ¶
conn, err := Dial(context.TODO(), "ws://example.com") if err != nil { // Make sure to handle the error return } defer conn.Close() // Create a single client with the connection. // There is no need to create multiple connections or clients // because it will all be managed for you.
Output:
type ConnOption ¶
type ConnOption interface { DialOption ServerOption }
ConnOption represents a configuration that applies symmetrically on both sides, client and server.
func WithCompression ¶
func WithCompression(mode CompressionMode, threshold int) ConnOption
WithCompression configures compression over the WebSocket. By default, compression is disabled and for now is considered an experimental feature.
func WithMessageType ¶ added in v0.4.0
func WithMessageType(typ MessageType) ConnOption
WithMessageType allows users to set the underlying WebSocket message encoding. Default is MessageBinary.
Note: for browser clients like Apollo GraphQL the MessageText encoding should be used.
type ConnectParams ¶ added in v0.5.0
type ConnectParams struct { // Backoff specifies the configuration options for connection backoff. Backoff backoff.Config // MinConnectTimeout is the minimum amount of time we are willing to give a // connection to complete. MinConnectTimeout time.Duration }
ConnectParams defines the parameters for connecting and retrying. Users are encouraged to use this instead of the BackoffConfig type defined above. See here for more details: https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
This API is EXPERIMENTAL.
type DialOption ¶
type DialOption interface {
SetDial(*dialOpts)
}
DialOption configures how we set up the connection.
func WithConnectParams ¶ added in v0.5.0
func WithConnectParams(p ConnectParams) DialOption
WithConnectParams configures the client to use the provided ConnectParams.
func WithHTTPClient ¶
func WithHTTPClient(client *http.Client) DialOption
WithHTTPClient provides an http.Client to override the default one used.
func WithHeaders ¶
func WithHeaders(headers http.Header) DialOption
WithHeaders adds custom headers to every dial HTTP request.
type ErrUnexpectedMessage ¶
type ErrUnexpectedMessage struct { // Expected was the expected message type. Expected string // Received was the received message type. Received string }
ErrUnexpectedMessage represents a unexpected message type.
func (ErrUnexpectedMessage) Error ¶
func (e ErrUnexpectedMessage) Error() string
Error implements the error interface.
type ErrUnsupportedMsgType ¶ added in v0.5.0
type ErrUnsupportedMsgType string
ErrUnsupportedMsgType represents an unsupported message type, per the GraphQL over Websocket protocol.
func (ErrUnsupportedMsgType) Error ¶ added in v0.5.0
func (e ErrUnsupportedMsgType) Error() string
Error implements the error interface.
type Handler ¶
Handler is for handling incoming GraphQL queries. All other "GraphQL over Websocket" protocol messages are automatically handled internally.
All resolvers errors should be included in *Response and any validation error should be returned as error.
type HandlerFunc ¶
HandlerFunc is an adapter to allow the use of ordinary functions as Request handlers.
func (HandlerFunc) ServeGraphQL ¶
func (f HandlerFunc) ServeGraphQL(s *Stream, req *Request) error
ServeGraphQL implements the Handler interface.
type MessageType ¶ added in v0.4.0
type MessageType websocket.MessageType
MessageType represents the type of a Websocket message.
type Request ¶
type Request struct { Query string `json:"query"` Variables map[string]interface{} `json:"variables"` OperationName string `json:"operationName"` }
Request represents a payload sent from the client.
type Response ¶
type Response struct { Data json.RawMessage `json:"data"` Errors []json.RawMessage `json:"errors"` }
Response represents a payload returned from the server. It supports lazy decoding by leaving the inner data for the user to decode.
type ServerError ¶
type ServerError struct {
Msg string `json:"msg"`
}
ServerError represents a payload which is sent by the server if it encounters a non-GraphQL resolver error.
func (*ServerError) Error ¶
func (e *ServerError) Error() string
Error implements the error interface.
type ServerOption ¶
type ServerOption interface {
SetServer(*options)
}
ServerOption allows the user to configure the handler.
func WithKeepAlive ¶ added in v0.5.0
func WithKeepAlive(period time.Duration) ServerOption
WithKeepAlive configures the server to send a GQL_CONNECTION_ACK message periodically to keep the client connection alive.
func WithOrigins ¶
func WithOrigins(origins ...string) ServerOption
WithOrigins lists the host patterns for authorized origins. The request host is always authorized. Use this to allow cross origin WebSockets.
type Stream ¶
type Stream struct {
// contains filtered or unexported fields
}
Stream is used for streaming responses back to the client.
type Subscription ¶
type Subscription struct {
// contains filtered or unexported fields
}
Subscription represents a stream of results corresponding to a GraphQL subscription query.
func (*Subscription) Recv ¶
func (s *Subscription) Recv(ctx context.Context) (*Response, error)
Recv is a blocking call which waits for either a response from the server or the context to be cancelled. Context cancellation does not cancel the subscription as a whole just the current recv call.
Recv is safe for concurrent use but it is a first come first serve basis. In other words, the response is not duplicated across all receivers.
func (*Subscription) Unsubscribe ¶
func (s *Subscription) Unsubscribe() error
Unsubscribe tells the server to stop sending anymore results and cleans up any resources associated with the subscription.
Directories ¶
Path | Synopsis |
---|---|
Package backoff provides configuration options for backoff.
|
Package backoff provides configuration options for backoff. |
internal
|
|
backoff
Package backoff implement the backoff strategy for gws.
|
Package backoff implement the backoff strategy for gws. |
rand
Package rand implements math/rand functions in a concurrent-safe way with a global random source, independent of math/rand's global source.
|
Package rand implements math/rand functions in a concurrent-safe way with a global random source, independent of math/rand's global source. |