Adding Graphql Subscriptions with HotChocolate and Redis

Published on

Last Updated on

Estimated Reading Time: 2 min

GraphQL offers us real-time capabilities to notify us when a specific event happens.

In this tutorial, we will add subscriptions to our graphql server. We will create an onPing event that will send notifications whenever we ping the server with an input string.

We will also use Redis that runs in a docker container, and see how we can listen to the events in a Redis command shell.

Running Redis using docker

docker run -d --name=redis-graphql -p 6379:6379 redis:6.0-alpine

Adding a Subscription

We will add a new class Subscription which will have our onPing event.

public class Subscription : ObjectType
{
    protected override void Configure(IObjectTypeDescriptor descriptor)
    {
        descriptor.Field("onPing")
            .Type<NonNullType<StringType>>()
            .Resolve(ctx => ctx.GetEventMessage<string>())
            .Subscribe(async ctx => await ctx.Service<ITopicEventReceiver>().SubscribeAsync<string, string>("onPing", ctx.RequestAborted));
    }
}
  • Line 5: The topic for the subscription.
  • Line 7: Gets the message payload.
  • Line 8: Subscribes to the pub/sub system

To use subscriptions, we need to add the WebSockets middleware to our request pipeline.

// Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseWebSockets();
}

Note: Middleware order is important with .NET Core, so this middleware needs to come before the GraphQL middleware.

Next, We need to register our subscriptions and set up the Redis subscription provider in the Startup class.

public void ConfigureServices(IServiceCollection services)
{
    var options = new ConfigurationOptions
    {
        EndPoints = { "localhost:6379" },
    };

    services.AddGraphQLServer()
            .AddSubscriptionType<Subscription>();
            .AddRedisSubscriptions(_ => ConnectionMultiplexer.Connect(options));
}

Note: Redis is on port 6379 by default.

Publishing the event

Our mutation needs to use ITopicEventSender to send messages to the underlying pub/sub system.

public class Mutation : ObjectType
{
    protected override void Configure(IObjectTypeDescriptor descriptor)
    {
        descriptor.Field("ping")
            .Type<NonNullType<StringType>>()
            .Argument("payload", des => des.Type<NonNullType<StringType>>())
            .Resolve(
                async ctx =>
                {
                    var input = ctx.ArgumentValue<string>("payload");

                    await ctx.Service<ITopicEventSender>().SendAsync("onPing", input);
                    return input;
                });
    }
}
  • Line 13:: "onPing" is the topic we want to publish, and input is our payload.

Testing

We can start an Interactive Shell for the Redis Docker Container.

docker exec -it redis-graphql redis-cli

Next, we can subscribe to the onPing channel using the subscribe command.

subscribe onPing

Subscribing to the topic in an interactive shell for redis

In Insomnia (or your IDE of choice), add the following mutation.

mutation {
    ping(payload: "hello")
}

On execution, we should see the following responses.

Mutation and response

In the shell, we can see the event on Redis.

Event caught by Redis listener

Demo Project

There is a demo project you can check out over on GitHub.