Search
K
  1. Server Sent Events

Server Sent Events

Server Sent Events (SSE) can be used to push real-time data down to the web browser in an async manner without blocking threads using the IAsyncIEnumerable interface like so:

Endpoint.cs
public class EventStream : EndpointWithoutRequest
{
    public override void Configure()
    {
        Get("event-stream");
        AllowAnonymous();
        Options(x => x.RequireCors(p => p.AllowAnyOrigin()));
    }

    public override async Task HandleAsync(CancellationToken ct)
    {
        //simply provide any IAsyncEnumerable<T> for the 2nd argument
        await Send.EventStreamAsync("my-event", GetDataStream(ct), ct);
    }

    private async IAsyncEnumerable<object> GetDataStream([EnumeratorCancellation] CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
        {
            await Task.Delay(1000);
            yield return new { guid = Guid.NewGuid() };
        }
    }
}

In the browser, the event stream can be subscribed to and consumed using the EventSource object like so:

Index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
</head>
<body>
<script>
    const sse = new EventSource('http://localhost:8080/event-stream');
    sse.addEventListener('my-event', (e) => console.log(e.data));
</script>
</body>
</html>

The above example demonstrates sending a continuous stream of a single event model type. It is possible to send different types of data in a single stream with the use of the wrapper type StreamItem like so:

public override async Task HandleAsync(CancellationToken c)
{
    await Send.EventStreamAsync(GetMultiDataStream(c), c);

    async IAsyncEnumerable<StreamItem> GetMultiDataStream([EnumeratorCancellation] CancellationToken ct)
    {
        long id = 0;

        while (!ct.IsCancellationRequested)
        {
            await Task.Delay(1000);

            id++;

            if (DateTime.Now.Second % 2 == 1)
                yield return new StreamItem(id.ToString(), "odd-second", Guid.NewGuid()); //guide data
            else
                yield return new StreamItem(id.ToString(), "even-second", "hello!"); //string data
        }
    }
}

If you are planning to create more than a handful of server-sent-event streams, it's a good idea to enable HTTP2 in kestrel and all upstream servers such as reverse proxies and CDNs so that data can be multiplexed between the web server and client using a low number of tcp connections.

Here's a good read on the subject.


© FastEndpoints 2025