This article will implement a small demo on subscriptions in Hot Chocolate Graphql using the application In-Memory storage.
Since our target storage is application In-Memory, so this approach only apt for a single server hosted application. For applications with multiple servers then we have to use Redis Store.
GraphQL Subscription Flow:
In general, events raising or publishing will be triggered from the GraphQL mutation resolvers. Inside mutation resolvers raise the event to store the data into application in-memory storage. In GraphQL 'Subscriptions' is one of the root definitions like 'Query', Mutation'. So the subscription resolvers will always receive the data from in-memory storage. So the subscription resolvers can also be called subscribers whose job always watches the data send by the raised events.So the event raised from the mutation resolver will be received by all subscribers.
Create A .Net5 Web API App:
Now let's begin our journey by creating a .Net5 Web API sample application.
Install Hot Chocolate GraphQL Library:
Package Manager Command:
Install-Package HotChocolate.AspNetCore -Version 11.0.9
.Net CLI Command:
dotnet add package HotChocolate.AspNetCore --version 11.0.9
Register GrapqhQL Server And Endpoint:
In 'Startup.cs' we have to register the GraphQL server.
Startup.cs:(ConfigureService Method)
services.AddGraphQLServer();In 'Startup.cs' we have to register the GraphQL endpoint.
Startup.cs:(Configure Method)
app.UseEndpoints(endpoints => { endpoints.MapGraphQL(); endpoints.MapControllers(); });
Register In-Memory Subscription Service:
Application In-Memory storage used by the GraphQL Subscriptions. So need to register the in-memory subscription service.
Startup.cs:(ConfigureService Method)
services .AddGraphQLServer() .AddInMemorySubscriptions();
Register WebSockets Middleware:
GraphQL subscriptions by default work over WebSockets. So let's add the WebSockets middleware.
Startup.cs:(Configure Method)
app.UseWebSockets(); app.UseRouting();
Create Subscription Type:
Now we will create a subscription type and resolver method that will give a response to the client subscribers.
Let's create a sample response model class like 'Product.cs' into the new folder 'Models'.
Models/Product.cs:
using System; namespace Hc.PubSub.Models { public class Product { public string Name { get; set; } public decimal Price { get; set; } public DateTime CreatedDate { get; set; } } }Now create a folder like 'Core' and add our new class that defines the subscriptions type.
Core/SubscriptionObjectType.cs:
using HotChoco.Pub.Sub.InMemo.Models; using HotChocolate; using HotChocolate.Types; namespace HotChoco.Pub.Sub.InMemo.Core { public class SubscriptionObjectType { [Topic] [Subscribe] public Product SubscribeProduct([EventMessage] Product product) { return product; } } }
- (Line: 10) Decorated with 'HotChocolate.Types.Topic' attribute. This attribute makes the resolver method name 'SubscribeProduct' as the pub/sub channel name.
- (Line: 11) Decorated with 'HotChocolate.Types.Subscribe'. This makes our resolver as listner of the pub/sub channel.
- The 'EventMessage' attribute injects the payload data into our subscribe resolver method.
- Finally simply returning the 'Product' object as output. So all subscriber client apps will receive the 'Product' payload.
Startup.cs:
services .AddGraphQLServer() .AddInMemorySubscriptions() .AddSubscriptionType<SubscriptionObjectType>();
Create Mutation Type And Invoke EventSender:
Now we have to create mutation resolver method in which we are going to invoke the event sender of the pub/sub.
Now let's create a new mutation type like 'MutationObjectType.cs'.
Core/MutationObjectType.cs:
using System.Threading.Tasks; using HotChoco.Pub.Sub.InMemo.Models; using HotChocolate; using HotChocolate.Subscriptions; namespace HotChoco.Pub.Sub.InMemo.Core { public class MutationObjectType { public async Task<string> AddProduct( [Service] ITopicEventSender eventSender, Product model) { // add your own logic to saving data into some data store. model.CreatedDate = DateTime.Now; await eventSender.SendAsync(nameof(SubscriptionObjectType.SubscribeProduct), model); return model.Name; } } }
- Here injecting 'HotChocolate.Subscriptions.ITopicEventSender', to raise the pub/sub channel sender event. So for sending here we have 2 input params like 'topic'(which can be called as channel name) and 'model'(payload object). Here channel name or topic value is our subscription resolver method name.
Startup.cs:
services .AddGraphQLServer() .AddInMemorySubscriptions() .AddSubscriptionType<SubscriptionObjectType>() .AddMutationType<MutationObjectType>();
Create A Query Type:
Actually for our demo sample we don't have any work with query type, but without query type we will encounter the error to start our application.. so let's create some dummy query resolver.
src/QueryObjectType.cs:
namespace HotChoco.Pub.Sub.InMemo.Core { public class QueryObjectType { public string Test() { return "hello"; } } }Register query type in the 'Startup.cs'.
Startup.cs:
services .AddGraphQLServer() .AddInMemorySubscriptions() .AddSubscriptionType<SubscriptionObjectType>() .AddMutationType<MutationObjectType>() .AddQueryType<QueryObjectType>();
Test Pub/Sub:
Now run the application and navigate to '/graphql'. Open 2 different tabs on the browser one act as publisher and another tab act as subscriber.
The publisher tab, must work with mutation which raises the sender event. So let's frame the mutation like below.
Mutation:
mutation($myProduct: ProductInput){ addProduct(model:$myProduct) }Variable:
{ "myProduct":{ "name":"tt", "cost": 1000 } }
The subscriber tab,must work with our graphql subscription, which watches for the data from the server. So lets frame the subscription request like below.
Subscriptions:
subscription{ subscribeProduct{ name, cost, createdDate } }So that all about the implementation steps for pub/sub flow in GraphQL.
Video Session:
Support Me!
Buy Me A Coffee
PayPal Me
Wrapping Up:
Hopefully, I think this article delivered some useful information on pub/sub flow in GraphQL endpoint using in-memory storage. I love to have your feedback, suggestions, and better techniques in the comment section below.
Your website is not showing the examples, as what is written is the same color as the background.
ReplyDeleteIt showing
DeleteMight taking long time for you, so please wait or reload the page
Is it possible to publish subscription messages without having to go through a mutation first?
ReplyDeleteI have background services mutating data that I would like to push out via subs.
trigering only subscription not possible as for as i know
DeleteI've got error: The specified operation kind is not allowed.
ReplyDeleteSorry, GET method was selected accidentally
Deletei am also getting this error. how did u resolve it?
DeleteAnyone who is getting error "The specified operation kind is not allowed", make sure to turn off Banana Cake Pop's "Use Http Get". Otherwise it'll send get for both get, and post requests.
ReplyDelete