Subscriptions๐
In this section, we'll cover how you can add subscriptions to your schema. Subscriptions are a way to get real-time updates from your server through your GraphQL Schema.
Setup๐
To use subscriptions, you'll need to turn on Undine's async support, and use the channels integration. This will set you up with a web server capable of GraphQL over WebSocket protocol. You'll also need a client capable of using the protocol.
Now, you can create a new RootType called Subscription
and add Entrypoints to it. Let's go over the different
Entrypoint references that can be used to create subscriptions.
AsyncGenerators๐
The simplest way of creating subscriptions is by using an AsyncGenerator function.
Let's take a look at a simple example of a subscription that counts down from 10 to 0.
About method signature
A method decorated with @Entrypoint is treated as a static method by the Entrypoint.
The self argument is not an instance of the RootType,
but root argument of the GraphQLField resolver. To clarify this,
it's recommended to change the argument's name to root,
as defined by the RESOLVER_ROOT_PARAM_NAME
setting.
The value of the root argument for an Entrypoint is None by default,
but can be configured using the ROOT_VALUE
setting if desired.
The info argument can be left out, but if it's included, it should always
have the GQLInfo type annotation.
This will create the following subscription in the GraphQL schema:
Using this subscription, you'll receive the following response 10 times on 1 second intervals,
while the value of the countdown field is decreases from 10 to 1.
The subscription's output type will be determined based on the first generic type parameter
on the AsyncGenerator return type (in this case int), so typing it is required.
To add arguments for the subscription, you can add them to the function signature. Typing these arguments is also required to determine their input type.
This will create the following subscription in the GraphQL schema:
If an exception is raised in the function, the subscription will be closed
and an error message will be sent to the client. You should raise exceptions
subclassing GraphQLError for better error messages, or use the GraphQLErrorGroup
to raise multiple errors at once.
You can also yield a GraphQLError from the function, which will send
an error while keeping the subscription open. Furthermore, adding the error to the return
type does not change the return type of the subscription.
AsyncIterables๐
You can also use an AsyncIterable instead of creating an AsyncGenerator function.
Note that the AsyncIterable needs to be returned from the Entrypoint function,
not used as the Entrypoint reference itself. Otherwise, they work similarly to
AsyncGenerators.
Signal subscriptions๐
Undine also supports creating subscriptions for Django signals
using SignalSubscriptions. For example, if you wanted to listen to new Tasks
being created, you could add a ModelCreateSubscription for the Task Model like this.
Similar subscriptions exists for Model updates (ModelUpdateSubscription), deletes (ModelDeleteSubscription),
and overall saves (ModelSaveSubscription). These subscriptions return data through QueryTypes
so queries to them are optimized just like any other query.
For delete subscriptions, note that the Model instance may have been deleted by the time the subscription is executed, so you should not rely on the instance existing in the database or its relations being connected like you would with a normal query.
For other signals, you can create custom subscriptions by subclassing undine.subscriptions.SignalSubscription
and adding the appropriate converters in order to use it in your schema.
See the "Hacking Undine" section for more information on how to do this.
Permissions๐
As subscriptions use Entrypoints, you can use their permission checks
to set per-value permissions for the subscription. Raising an exception from
a permission check will close the subscription and send an error message
to the client.
You can also configure permission checks for establishing a websocket connection
using the WEBSOCKET_CONNECTION_INIT_HOOK
setting.