Search code examples
.netgraphqlstreamhotchocolateminimal-apis

Streaming data using HotChocolate GraphQl for .NET project - all results at once instead of one by one problem


I'm using C#, .NET7 minimal API and HotChocolate GraphQl. I'm trying to setup graphQl simple query endpoint which will be able to return stream like this:

public async IAsyncEnumerable<string> GetDummyNumbers()
{
    for (var i = 0; i < 10; i++)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        yield return i.ToString();
    }
}

It works as is but even though it's IAsyncEnumerable, by default it's waiting until all values are resolved and then sends all of them at once to the client.

Solutions I've found which could've been partially useful:

  1. There is '@stream' directive for GraphQl but it's not working out of the box, probably some configuration or implementation is required but I didn't find resources how to achieve this.
  2. Subscriptions could work but it's not exactly what I need, because I don't need real-time data, I need only to stream data per request.

I tried to check official package documentation but I didn't find anything which would help me resolve that. Also looking for any similar problem or examples didn't gave me an answer.

I would appreciate any help which will get me closer to solution when I can call this endpoint and get results immediately when available one by one.


Solution

  • I researched this question in detail when I ran into a similar problem, and this is what I found:

    Firstly, to enable streaming in the latest version of HotChocolate (13.4.0 at the time of writing), you need to add the following code (link):

    services.AddGraphQLServer()
        .ModifyOptions(o =>
        {
            o.EnableStream = true;
        })
        .AddQueryType<Query>()
        ...
    
    

    Then, in the request class, mark your method with the [StreamResult] attribute, and after that you need to make the return type more complex (when checking on strings, streaming did not work)

    public class Query
    {
        [StreamResult]
        public async IAsyncEnumerable<Result> GetDummyNumbers()
        {
            for (var i = 0; i < 5; i++)
            {
                await Task.Delay(TimeSpan.FromSeconds(1));
                yield return new Result(i);
            }
        }
    
        public record Result(int id);
    }
    

    after completing these steps, you can write a query using the directive @stream and make sure everything works :)

    query test {
      dummyNumbers @stream {
        id
      }
    }