Introduction

Streaming is a powerful feature that allows APIs to handle large volumes of data and real-time updates efficiently. In Goa, streaming support enables you to define endpoints that can send or receive a sequence of results, enhancing the responsiveness and scalability of your services.

Why Streaming Matters

  • Efficiency: Streaming reduces the overhead of multiple HTTP requests by allowing continuous data transmission over a single connection.
  • Real-Time Data: Enables real-time data updates, which are essential for applications like live feeds, notifications, and data monitoring.
  • Scalability: Handles large datasets more gracefully by processing data in chunks rather than loading entire datasets into memory.
  • Improved User Experience: Provides a smoother and more responsive experience for users by delivering data incrementally.

Goa’s Streaming Capabilities

When working with large files or real-time data streams, loading entire payloads into memory before processing them isn’t always feasible or desirable. Goa provides multiple approaches to handling streaming data:

Using StreamingPayload and StreamingResult when you need to:

  • Stream structured data with known types
  • Leverage Goa’s type system and validation
  • Work with gRPC streaming

Using SkipRequestBodyEncodeDecode and SkipResponseBodyEncodeDecode when you need to:

  • Stream raw binary data or unknown content types
  • Implement custom streaming protocols
  • Process multimedia streams

Goa supports unidirectional and bidirectional streaming over different transport protocols, including HTTP (using WebSockets) and gRPC. By leveraging Goa’s Domain-Specific Language (DSL), you can define streaming endpoints that are transport-agnostic, allowing seamless integration and flexibility in your service architecture.

Key Features

  • Unidirectional Streaming: Allows either the server or the client to send a stream of data.
  • Bidirectional Streaming: Enables both the server and the client to send streams of data simultaneously.
  • Transport Independence: Defines streaming logic that works across multiple transport protocols without modification.
  • Generated Stream Interfaces: Automatically generates server and client stream interfaces based on your streaming definitions.
  • Custom Views: Supports multiple views for streamed results, providing flexibility in how data is presented to clients.

Example Overview

Let’s explore a logger service that manages log entries. We’ll demonstrate different streaming scenarios that showcase real-world use cases. See section Stream raw binary data over HTTP for an example of how to stream raw binary data over HTTP.

Server-Side Streaming Example

var _ = Service("logger", func() {
    // Server streams log entries for a specific topic
    Method("subscribe", func() {
        Description("Stream log entries for a specific topic")
        
        Payload(func() {
            Field(1, "topic", String, "Topic of the log to subscribe to")
            Required("topic")
        })
        
        // Server streams log entries
        StreamingResult(func() {
            Field(1, "timestamp", String, "Time of reading")
            Field(2, "message", String, "Log message")
            Required("timestamp", "message")
        })

        HTTP(func() {
            GET("/logs/{topic}/stream")
            Response(StatusOK)
        })
    })
})

Bidirectional Streaming Example

var _ = Service("logger", func() {
    // Real-time inventory management with bidirectional streaming
    Method("subscribe", func() {
        Description("Bidirectional stream for real-time log updates and topic management")
        
        // Client streams inventory updates
        StreamingPayload(func() {
            Field(1, "topic", String, "Topic of the log to subscribe to")
            Required("topic")
        })
        
        // Server streams inventory status and alerts
        StreamingResult(func() {
            Field(1, "timestamp", String, "Time of reading")
            Field(2, "message", String, "Log message")
            Required("timestamp", "message")
        })

        HTTP(func() {
            GET("/logs/{topic}/stream")
            Response(StatusOK)
        })
    })
})

These examples demonstrate:

1. Server-Side Streaming

A one-way streaming pattern where the server sends multiple responses to a single client request.

Example: Log Monitoring

Client Request (once): "Monitor logs for 'database errors'"

Server Responses (continuous):
10:00:01 - Database connection timeout
10:00:05 - Query execution failed
10:00:08 - Connection pool exhausted
(continues sending new logs as they occur)

Key Characteristic: After the initial request, data only flows from server to client.

2. Bidirectional Streaming

A pattern allowing both sides to send multiple messages over time.

Example: Interactive Log Management

Client: "Start monitoring logs for 'database'"
Server: *sends database-related logs*
Client: "Update filter to include 'network' too"
Server: *sends both database and network logs*
Client: "Remove 'database' filter"
Server: *sends only network logs*
(both sides continue communication)

Key Characteristic: Enables ongoing back-and-forth communication, where both client and server can send multiple messages throughout the connection’s lifetime.

Next Steps

With this introduction to streaming in Goa, you are now ready to delve deeper into designing, implementing, and managing streaming endpoints. The subsequent sections will guide you through: