DEV Community

Eduardo Julião
Eduardo Julião

Posted on

3 1

Event Stream - Server Side

For this post, we're going to explore a way to stream data to the client using a Web Api and C#.

Picture this requirement, you need to process an array of data, lets say of a 100 items, once each item is processed, the api should return data to the client, without SignalR.

To do this, in this example, we will have a worker that will return a IAsyncEnumerable and between each iteration, the method will wait between 5 to 10 seconds.

//Service.cs
private static readonly Random _random = new Random();

public async IAsyncEnumerable<int> GetData(int amount)
{
    foreach(var item in Enumerable.Range(0, amount))
    {
        // Wait a random amount of time to simulate some work.
        var secondsToWait = _random.Next(5,10);
        await Task.Delay(Timespan.FromSeconds(secondsToWait));
        yield return item;
    }
}
Enter fullscreen mode Exit fullscreen mode

With the simulated service in place, we can move on to the Controller. For the endpoint, we're going to have an async Task Get method that will write the result to the Response and send it for every item it was processed.

// StreamController.cs
private readonly IService _service;

public StreamController(IService service)
{
    _service = service;
}

[HttpGet("{amount:int:min(1)}")]
public async Task GetStreamData(int amount)
{
    // Set Content-Type to text/event-stream
    Response.Headers.Add("Content-Type", "text/event-stream");

    await foreach(var data in _service.GetData(amount))
    {
        // this can be anything, even just a json object
        string dataItem = $"data: {data}\n\n";

        // Convert the text to a byte array
        var utf8DataitemBytes = Encoding.UTF8.GetBytes(dataItem);

        // Write the byte array to the HttpResponse
        await Response.Body.WriteAsync(utf8DataitemBytes, 0, utf8DataitemBytes.Length);
        // Push
        await Response.Body.FlushAsync();
    }
}
Enter fullscreen mode Exit fullscreen mode

When the endpoint is called, it'll send a new "line" of data instead of waiting for all items to be processed to be sent at once.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more →

Top comments (2)

Collapse
 
_hs_ profile image
HS

HTTP is using TCP/IP (for now mainly). So the please fix that part "without TCP/IP" as it's missleading. And it's not returning HttpRequest it's using it to get the response object and write to it.

Collapse
 
eduardojuliao profile image
Eduardo Julião

Thanks for the feedback!
Yes, reading through that part again it was misleading.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more