A websocket transport for GraphQL subscriptions
npm install @httptoolkit/subscriptions-transport-ws_This is a fork of subscriptions-transport-ws, updating ws to fix security issues. This is a quick fix, and won't be maintained for general use outside Mockttp in future_
 
(Work in progress!)
A GraphQL WebSocket server and client to facilitate GraphQL queries, mutations and subscriptions over WebSocket.
> subscriptions-transport-ws is an extension for GraphQL, and you can use it with any GraphQL client and server (not only Apollo).
Start by installing the package, using Yarn or NPM.
Using Yarn:
$ yarn add @httptoolkit/subscriptions-transport-ws
Or, using NPM:
$ npm install --save @httptoolkit/subscriptions-transport-ws
> Note that you need to use this package on both GraphQL client and server.
> This command also installs this package's dependencies, including graphql-subscriptions.
Starting with the server, create a new simple PubSub instance. We will later use this PubSub to publish and subscribe to data changes.
``js
import { PubSub } from 'graphql-subscriptions';
export const pubsub = new PubSub();
`
Now, create SubscriptionServer instance, with your GraphQL schema, execute and subscribe (from graphql-js package):
`js
import { createServer } from 'http';
import { SubscriptionServer } from '@httptoolkit/subscriptions-transport-ws';
import { execute, subscribe } from 'graphql';
import { schema } from './my-schema';
const WS_PORT = 5000;
// Create WebSocket listener server
const websocketServer = createServer((request, response) => {
response.writeHead(404);
response.end();
});
// Bind it to port and start listening
websocketServer.listen(WS_PORT, () => console.log(
Websocket Server is now running on http://localhost:${WS_PORT}
));
const subscriptionServer = SubscriptionServer.create(
{
schema,
execute,
subscribe,
},
{
server: websocketServer,
path: '/graphql',
},
);
`
Please refer to graphql-subscriptions documentation for how to create your GraphQL subscriptions, and how to publish data.
When using this package for client side, 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.
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:
`js
import { SubscriptionClient } from '@httptoolkit/subscriptions-transport-ws';
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,
});
`
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:
`js
import {SubscriptionClient, addGraphQLSubscriptions} from '@httptoolkit/subscriptions-transport-ws';
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:
`js
apolloClient.subscribe({
query: gql
subscription onNewItem {
newItemCreated {
id
}
},`
variables: {}
}).subscribe({
next (data) {
// Notify your application with the new arrived data
}
});
`js
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/@httptoolkit/subscriptions-transport-ws@VERSION/browser/client.js
> Replace VERSION with the latest version of the package.
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:
`jsYOUR_SUBSCRIPTION_ENDPOINT_HERE
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
subscriptionsEndpoint: ,`
}));
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:
`html`
Then, create SubscriptionClient and define the fetcher:
`js`
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:
`js`
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
);
: 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
* minTimeout?: number: the minimum amount of time the client should wait for a connection to be made (default 1000 ms)
* lazy?: boolean : use to set lazy mode - connects only when first subscription created, and delay the socket initialization
* connectionParams?: Object | Function | Promise