0.9.15 • Published 6 years ago

subscription-client v0.9.15

Weekly downloads
4
License
MIT
Repository
github
Last release
6 years ago

npm version GitHub license

subscription-client

A GraphQL WebSocket client to facilitate GraphQL queries, mutations and subscriptions over WebSocket.

subscription-client is an extension for GraphQL, and you can use it with any GraphQL client (not only Apollo).

Getting Started

Start by installing the package, using Yarn or NPM.

Using Yarn:
$ yarn add subscription-client

Or, using NPM:
$ npm install --save subscription-client

This command also installs this package's dependencies, including graphql-subscriptions.

Client

You can choose either use HTTP request for Queries and Mutation and use the WebSocket for subscriptions only, or create a full transport that handles all type of GraphQL operations over the socket.

Full WebSocket Transport

To start with a full WebSocket transport, that handles all types of GraphQL operations, import and create an instance of SubscriptionClient.

Then, create your ApolloClient instance and use the SubscriptionsClient instance as network interface:

import { SubscriptionClient } from 'subscription-client';
import ApolloClient from 'apollo-client';

const GRAPHQL_ENDPOINT = 'ws://localhost:3000/graphql';

const client = new SubscriptionClient(GRAPHQL_ENDPOINT, {
  reconnect: true,
});

const apolloClient = new ApolloClient({
    networkInterface: client,
});

Hybrid WebSocket Transport

To start with a hybrid WebSocket transport, that handles only subscriptions over WebSocket, create your SubscriptionClient and a regular HTTP network interface, then extend your network interface to use the WebSocket client for GraphQL subscriptions:

import {SubscriptionClient, addGraphQLSubscriptions} from 'subscription-client';
import ApolloClient, {createNetworkInterface} from 'apollo-client';

// Create regular NetworkInterface by using apollo-client's API:
const networkInterface = createNetworkInterface({
 uri: 'http://localhost:3000' // Your GraphQL endpoint
});

// Create WebSocket client
const wsClient = new SubscriptionClient(`ws://localhost:5000/`, {
    reconnect: true,
    connectionParams: {
        // Pass any arguments you want for initialization
    }
});

// Extend the network interface with the WebSocket
const networkInterfaceWithSubscriptions = addGraphQLSubscriptions(
    networkInterface,
    wsClient
);

// Finally, create your ApolloClient instance with the modified network interface
const apolloClient = new ApolloClient({
    networkInterface: networkInterfaceWithSubscriptions
});

Now, when you want to use subscriptions in client side, use your ApolloClient instance, with subscribe or query subscribeToMore:

apolloClient.subscribe({
  query: gql`
    subscription onNewItem {
        newItemCreated {
            id
        }
    }`,
  variables: {}
}).subscribe({
  next (data) {
    // Notify your application with the new arrived data
  }
});
apolloClient.query({
  query: ITEM_LIST_QUERY,
  variables: {}
}).subscribeToMore({
  document: gql`
    subscription onNewItem {
        newItemCreated {
            id
        }
    }`,
  variables: {},
  updateQuery: (prev, { subscriptionData, variables }) => {
    // Perform updates on previousResult with subscriptionData
    return updatedResult;
  }
});

If you don't use any package/modules loader, you can still use this package, by using unpkg service, and get the client side package from:

https://unpkg.com/subscription-client@VERSION/browser/client.js

Replace VERSION with the latest version of the package.

Use it with GraphiQL

You can use this package's power with GraphiQL, and subscribe to live-data stream inside GraphiQL.

If you are using the latest version of graphql-server flavors (graphql-server-express, graphql-server-koa, etc...), you already can use it! Make sure to specify subscriptionsEndpoint in GraphiQL configuration, and that's it!

For example, graphql-server-express users need to add the following:

app.use('/graphiql', graphiqlExpress({
  endpointURL: '/graphql',
  subscriptionsEndpoint: `YOUR_SUBSCRIPTION_ENDPOINT_HERE`,
}));

If you are using older version, or another GraphQL server, start by modifying GraphiQL static HTML, and add this package and it's fetcher from CDN:

    <script src="//unpkg.com/subscription-client@0.5.4/browser/client.js"></script>
    <script src="//unpkg.com/graphiql-subscriptions-fetcher@0.0.2/browser/client.js"></script>

Then, create SubscriptionClient and define the fetcher:

let subscriptionsClient = new window.SubscriptionsTransportWs.SubscriptionClient('SUBSCRIPTION_WS_URL_HERE', {
  reconnect: true
});
let myCustomFetcher = window.GraphiQLSubscriptionsFetcher.graphQLFetcher(subscriptionsClient, graphQLFetcher);

graphQLFetcher is the default fetcher, and we use it as fallback for non-subscription GraphQL operations.

And replace your GraphiQL creation logic to use the new fetcher:

ReactDOM.render(
  React.createElement(GraphiQL, {
    fetcher: myCustomFetcher, // <-- here
    onEditQuery: onEditQuery,
    onEditVariables: onEditVariables,
    onEditOperationName: onEditOperationName,
    query: ${safeSerialize(queryString)},
    response: ${safeSerialize(resultString)},
    variables: ${safeSerialize(variablesString)},
    operationName: ${safeSerialize(operationName)},
  }),
  document.body
);

API Docs

SubscriptionClient

Constructor(url, options, webSocketImpl)

  • url: string : url that the client will connect to, starts with ws:// or wss://
  • options?: Object : optional, object to modify default client behavior
    • timeout?: number : how long the client should wait in ms for a keep-alive message from the server (default 30000 ms), this parameter is ignored if the server does not send keep-alive messages. This will also be used to calculate the max connection time per connect/reconnect
    • lazy?: boolean : use to set lazy mode - connects only when first subscription created, and delay the socket initialization
    • connectionParams?: Object | Function | Promise<Object> : object that will be available as first argument of onConnect (in server side), if passed a function - it will call it and send the return value, if function returns as promise - it will wait until it resolves and send the resolved value.
    • reconnect?: boolean : automatic reconnect in case of connection error
    • reconnectionAttempts?: number : how much reconnect attempts
    • connectionCallback?: (error) => {} : optional, callback that called after the first init message, with the error (if there is one)
    • inactivityTimeout?: number : how long the client should wait in ms, when there are no active subscriptions, before disconnecting from the server. Set to 0 to disable this behavior. (default 0)
  • webSocketImpl?: Object - optional, constructor for W3C compliant WebSocket implementation. Use this when your environment does not have a built-in native WebSocket (for example, with NodeJS client)

Methods

request(options) => Observable<ExecutionResult>: returns observable to execute the operation.

  • options: {OperationOptions}
    • query: string : GraphQL subscription
    • variables: Object : GraphQL subscription variables
    • operationName: string : operation name of the subscription
    • context: Object : use to override context for a specific call

unsubscribeAll() => void - unsubscribes from all active subscriptions.

on(eventName, callback, thisContext) => Function

  • eventName: string: the name of the event, available events are: connecting, connected, reconnecting, reconnected, disconnected and error
  • callback: Function: function to be called when websocket connects and initialized.
  • thisContext: any: this context to use when calling the callback function.
  • => Returns an off method to cancel the event subscription.

onConnected(callback, thisContext) => Function - shorthand for .on('connected', ...)

  • callback: Function: function to be called when websocket connects and initialized, after ACK message returned from the server
  • thisContext: any: this context to use when calling the callback function.
  • => Returns an off method to cancel the event subscription.

onReconnected(callback, thisContext) => Function - shorthand for .on('reconnected', ...)

  • callback: Function: function to be called when websocket reconnects and initialized, after ACK message returned from the server
  • thisContext: any: this context to use when calling the callback function.
  • => Returns an off method to cancel the event subscription.

onConnecting(callback, thisContext) => Function - shorthand for .on('connecting', ...)

  • callback: Function: function to be called when websocket starts it's connection
  • thisContext: any: this context to use when calling the callback function.
  • => Returns an off method to cancel the event subscription.

onReconnecting(callback, thisContext) => Function - shorthand for .on('reconnecting', ...)

  • callback: Function: function to be called when websocket starts it's reconnection
  • thisContext: any: this context to use when calling the callback function.
  • => Returns an off method to cancel the event subscription.

onDisconnected(callback, thisContext) => Function - shorthand for .on('disconnected', ...)

  • callback: Function: function to be called when websocket disconnected.
  • thisContext: any: this context to use when calling the callback function.
  • => Returns an off method to cancel the event subscription.

onError(callback, thisContext) => Function - shorthand for .on('error', ...)

  • callback: Function: function to be called when an error occurs.
  • thisContext: any: this context to use when calling the callback function.
  • => Returns an off method to cancel the event subscription.

close() => void - closes the WebSocket connection manually, and ignores reconnect logic if it was set to true.

use(middlewares: MiddlewareInterface[]) => SubscriptionClient - adds middleware to modify OperationOptions per each request

  • middlewares: MiddlewareInterface[] - Array contains list of middlewares (implemented applyMiddleware method) implementation, the SubscriptionClient will use the middlewares to modify OperationOptions for every operation

status: number : returns the current socket's readyState

How it works?

The current version of this transport, also support a previous version of the protocol.

You can find the old protocol docs here