Subscriptions

Subscriptions are a way for a client to request notifications about arbitrary events defined by the server; this parallels how a query exposes arbitrary data defined by the server.

The essential support for GraphQL subscriptions is in the main Lacinia library.

Lacinia-Pedestal’s subscription support is designed to be compatible with Apollo GraphQL, a popular library in the JavaScript domain [1]. Like Apollo, Lacinia-Pedestal uses WebSockets to create a durable connection between the client and the server.

Overview

A client (typically, a web browser or mobile phone) will establish a connection to the server, and convert it to a full-duplex WebSocket connection.

This single WebSocket connection will be multiplexed to handle any number of subscription requests from the client.

When a subscription is requested, a streamer defined in the GraphQL schema is invoked. A streamer is similar to a field resolver; it has two responsibilities:

  • Do whatever setup is necessary, then as new events are available, provide data to a source stream callback function.
  • Return a cleanup function that shuts down whatever was previously set up.

Most commonly, a streamer will subscribe to some external feed such as a JMS or Kafka queue, or perhaps a core.async pub or channel.

When a streamer passes nil to the callback, a clean shutdown of the subscription occurs; the client is sent a completion message. The completion message informs the client that the stream of events has completed, and that it should not attempt to reconnect.

The definition of “completed” here is entirely up to the application. For example, a field argument could specify the maximum number of values to stream, and the streamer can pass nil after sufficient values are streamed.

The cleanup function is invoked when the client closes the subscription, when the connection from the client is lost due to a network partition, or when the streamer passes nil to the callback.

Configuration

When using com.walmartlabs.lacinia.pedestal2/default-service, subscriptions are always enabled, but default-service is always intended to be replaced in a live application.

The underlying function com.walmartlabs.lacinia.pedestal2/enable-subscriptions does the work of enabling subscriptions; the function is passed subscription options:

The following keys are commonly used:

:subscriptions-path
Path to use in subscriptions WebSocket requests; defaults to /ws,
:keep-alive-ms
The interval at which keep-alive messages are sent to the client; defaults to 30 seconds.
:subscription-interceptors
A seq of interceptors used when processing GraphQL query, mutation, or subscription requests via the WebSocket connection. This is used when overriding the default interceptors.

Further options are described by listener-fn-factory.

Connection Parameters

When the client creates a connection, it may pass a payload in the connection_init message; this is the connection parameters, and is made available to the streamer and resolver in the context under the :com.walmartlabs.lacinia/connection-parameters key.

Endpoint

Subscriptions are processed on a second endpoint; normal requests continue to be sent to /api, but subscription requests must use /ws.

The /ws endpoint does not handle ordinary requests; instead it is used only to establish the WebSocket connection. From there, the client sends WebSocket text messages to initiate a subscription, and the server sends WebSocket text messages for subscription updates and keep alive messages.

Subscription requests are not allowed in /api path.

GraphiQL

GraphiQL, when enabled, is configured with subscriptions enabled; this means that GraphiQL can send subscription queries.

[1]

Apollo defines a particular contract for how the client and server communicate; this includes heartbeats, and an explicit way for the server to signal to the client that the subscription has completed.

The Apollo project also provides clients in several languages.