serverpod-streams

star 3.2k

Real-time streaming in Serverpod — Stream parameters and return types, WebSocket lifecycle, error handling. Use when building real-time features, chat, live updates, or WebSocket streaming.

serverpod By serverpod schedule Updated 5/12/2026

name: serverpod-streams description: Real-time streaming in Serverpod — Stream parameters and return types, WebSocket lifecycle, error handling. Use when building real-time features, chat, live updates, or WebSocket streaming.

Serverpod Streams

Endpoint methods that take or return Stream<T> get WebSocket-managed client stubs. Types must be serializable.

It's a common use case to use streams together with server events. See Server Events.

Defining a streaming method

class ExampleEndpoint extends Endpoint {
  Stream<String> echoStream(Session session, Stream<String> stream) async* {
    await for (var message in stream) {
      yield message;
    }
  }
}

Run serverpod generate for client stubs.

Client usage

var inStream = StreamController<String>();
var outStream = client.example.echoStream(inStream.stream);

outStream.listen((message) => print('Received: $message'));
inStream.add('Hello');

Close the StreamController when done. Cancelling the subscription closes both sides. We can use the in/out stream independently (often only need for stream from server). Methods may take multiple stream parameters when needed. Keep the API shape explicit and regenerate after changes.

Lifecycle

  • Each call creates a server Session; for return-stream methods, the session stays alive until the returned stream completes or is cancelled.
  • WebSocket disconnect closes streams on both sides.
  • Return-stream methods stay alive until cancelled or completed.

Error handling

Throwing a serializable exception closes the stream; the client receives it in onError. Exceptions can flow in both directions. Define exception types in .spy.yaml.

Example combined with server events (simplified)

class PixelDrawingEndpoint extends Endpoint {
  static const _channelPixelAdded = 'pixel-added';
  final _pixelData = Uint8List(_numPixels);

  Future<void> setPixel(
    Session session, {
    required int colorIndex,
    required int pixelIndex,
  }) async {
    _pixelData[pixelIndex] = colorIndex;

    // Notify all connected clients that we set a pixel, by posting a message
    // to the _channelPixelAdded channel.
    session.messages.postMessage(
      _channelPixelAdded,
      ImageUpdate(
        pixelIndex: pixelIndex,
        colorIndex: colorIndex,
      ),
    );
  }

  /// Returns a stream of image updates. The first message will always be a
  /// `ImageData` object, which contains the full image. Sequential updates
  /// will be `ImageUpdate` objects, which contains a single updated pixel.
  Stream imageUpdates(Session session) async* {
    var updateStream =
        session.messages.createStream<ImageUpdate>(_channelPixelAdded);

    yield ImageData(
      pixels: _pixelData.buffer.asByteData(),
      width: _imageWidth,
      height: _imageHeight,
    );

    await for (var imageUpdate in updateStream) {
      yield imageUpdate;
    }
  }
}

Deprecated pattern

The older streamOpened/handleStreamMessage/sendStreamMessage pattern and openStreamingConnection() still exists for legacy code but is deprecated. Use only streaming methods (Stream as parameter/return type) for new code.

Install via CLI
npx skills add https://github.com/serverpod/serverpod --skill serverpod-streams
Repository Details
star Stars 3,207
call_split Forks 364
navigation Branch main
article Path SKILL.md
More from Creator