Broadcasts
GraphQL subscription updates may broadcast data to multiple subscribers.
A broadcast is a subscription update which is executed once, then delivered to any number of subscribers. This reduces the time your server spends running GraphQL queries, since it doesn’t have to re-run the query for every subscriber.
But, take care: this approach risks leaking information to subscribers who shouldn’t receive it.
Setup
To enable broadcasts, add broadcast: true to your subscription setup:
class MyAppSchema < GraphQL::Schema # ... use SomeSubscriptionImplementation, broadcast: true # <---- end
Then, any broadcastable field can be configured with broadcastable: true:
field :name, String, null: false, broadcastable: true
When a subscription comes in where all of its fields are broadcastable: true, then it will be handled as a broadcast.
Additionally, you can set default_broadcastable: true:
class MyAppSchema < GraphQL::Schema # ... use SomeSubscriptionImplementation, broadcast: true, default_broadcastable: true # <---- end
With this setting, fields are broadcastable by default. Only a field with broadcastable: false in its configuration will cause a subscription to be handled on a subscriber-by-subscriber basis.
What fields are broadcastable?
GraphQL-Ruby can’t infer whether a field is broadcastable or not. You must configure it explicitly with broadcastable: true or broadcastable: false. (The subscription plugin also accepts default_broadcastable: true|false.)
A field is broadcastable if all clients who request the field will see the same value. For example:
-
General facts: celebrity names, laws of physics, historical dates
-
Public information: object names, document updated-at timestamps, boilerplate info
For fields like this, you can add broadcastable: true.
A field is not broadcastable if its value is different for different clients. For example:
-
Viewer-specific information: if a field is specifically viewer-based, then it can’t be broadcasted to other viewers. For example,
discussion { viewerCanModerate }might be true for a moderator, but it shouldn’t be broadcasted to other viewers. -
Context-specific information: if a field’s value takes the request context into consideration, it shouldn’t be broadcasted. For example, IP addresses or HTTP header values probably can’t be broadcasted. If a field reflects the viewer’s timezone, it can’t be broadcasted.
-
Restricted information: if some viewers see one value, while other viewers see a different value, then it’s not broadcastable. Broadcasting this data might leak private information to unauthorized clients. (This includes filtered lists: if the filtering is viewer-by-viewer, it’s not broadcastable.)
-
Fields with side effects: if the system requires a side effect (eg, logging a metric, updating a database, incrementing a counter) whenever a resolver is executed, it’s not a good candidate for broadcasting because some executions will be optimized away.
These fields can be tagged with broadcastable: false so that GraphQL-Ruby will handle them on a subscriber-by-subscriber basis.
If you want to use subscriptions but have a lot of non-broadcastable fields in your schema, consider building a new set of subscription fields with limited access to other schema objects. Instead, optimize those subscriptions for broacastability.
Under the Hood
GraphQL-Ruby determines which subscribers can receive a broadcast by inspecting:
-
Query string. Only exactly-matching query strings will receive the same broadcast.
-
Variables. Only exactly-matching variable values will receive the same broadcast.
-
Field and Arguments given to
.trigger. They must match the ones initially sent when subscribing. (Subscriptions always worked this way.) -
Subscription scope. Only clients with exactly-matching subscription scope can receive the same broadcasts.
So, take care to set subscription_scope whenever a subscription should be implicitly scoped!
(See GraphQL::Subscriptions::Event#fingerprint for the implementation of broadcast fingerprints.)
Checking for Broadcastable
For testing purposes, you can confirm that a GraphQL query string is broadcastable by using Subscriptions#broadcastable?:
subscription_string = "subscription { ... }" MySchema.subscriptions.broadcastable?(subscription_string) # => true or false
Use this in your application’s tests to make sure that broadcastable fields aren’t accidentally made non-broadcastable.
Connections and Edges
You can configure your generated Connection and Edge types to be broadcastable by setting default_broadcastable(true) in their definition:
# app/types/base_connection.rb class Types::BaseConnection < Types::BaseObject include GraphQL::Types::Relay::ConnectionBehaviors default_broadcastable(true) end # app/types/base_edge.rb class Types::BaseEdge < Types::BaseObject include GraphQL::Types::Relay::EdgeBehaviors default_broadcastable(true) end
(In your BaseObject, you should also have connection_type_class(Types::BaseConnection) and edge_type_class(Types::BaseEdge).)
PageInfo is broadcastable by default.