Designing Streaming Endpoints
Designing streaming endpoints in Goa involves defining methods that can handle the transmission of a sequence of results. Whether you’re implementing server-side streaming, client-side streaming, or bidirectional streaming, Goa’s DSL provides a clear and concise way to specify these behaviors.
Using the StreamingResult
DSL
The StreamingResult
DSL is used within a method definition to indicate that
the method will stream a sequence of results to the client. It is mutually
exclusive with the Result
DSL; you can only use one within a given method.
Example
var _ = Service("logger", func() {
Method("subscribe", func() {
// LogEntry instances are streamed to the client.
StreamingResult(LogEntry)
})
})
In this example:
- subscribe Method: Defines a streaming endpoint that sends
LogEntry
instances. - LogEntry: The type of the results that will be streamed to the client.
When defining a streaming method, you need to specify the type of data that will
be streamed. This is done by passing the result type to the StreamingResult
function.
Constraints and Considerations
- Mutual Exclusivity: A method can use either
Result
orStreamingResult
, but not both. - Single Result Type: All streamed results must be instances of the same type.
- Transport Independence: The design is agnostic of the transport protocol, allowing Goa to generate appropriate transport-specific code.
Using the StreamingPayload
DSL
The StreamingPayload
DSL is used within a method definition to indicate that
the method will receive a sequence of messages from the client. When used with
HTTP transport, it works in conjunction with the regular Payload
DSL to handle
both the initial connection parameters and the subsequent streaming data.
Example
var _ = Service("logger", func() {
Method("subscribe", func() {
// Client streams LogEntry instances
StreamingPayload(LogEntry)
// Single result returned after processing all updates
Result(Summary)
})
})
In this example:
- subscribe Method: Defines an endpoint that receives a stream of
LogEntry
instances. - LogEntry: The type of the payload that will be streamed from the client.
- Summary: A single result returned after processing all updates.
When defining a client streaming method, you need to specify the type of data
that will be received in the stream. This is done by passing the payload type to
the StreamingPayload
function.
Constraints and Considerations
- Single Payload Type: All streamed payloads must be instances of the same type.
- Transport Independence: The design is agnostic of the transport protocol, allowing Goa to generate appropriate transport-specific code.
- Optional Result: Client streaming methods may return a single result or no result.
- Transport Behavior: In HTTP, the stream is established after the initial request is processed and the connection is upgraded to WebSocket.
Combining with Regular Payload
For HTTP endpoints, you’ll often want to include initialization parameters in the
initial request before the WebSocket upgrade. This is achieved by combining
StreamingPayload
with a regular Payload
:
var _ = Service("logger", func() {
Method("subscribe", func() {
// Initial connection parameters
Payload(func() {
Field(1, "topic", String, "Topic of the log to subscribe to")
Field(2, "api_key", String, "API key for authentication")
Required("topic", "api_key")
})
// Stream of updates from the client
StreamingPayload(LogEntry)
// Final result after processing all updates
Result(Summary)
HTTP(func() {
GET("/logs/{topic}/stream")
Param("api_key")
})
})
})
This pattern is particularly useful for:
- Authentication and authorization before establishing the stream
- Providing context or scope for the streaming session
- Setting initial parameters that remain constant throughout the stream
- Validating the request before upgrading to a WebSocket connection
In the HTTP transport:
- The initial GET request includes the
topic
andapi_key
parameters - After validation, the connection is upgraded to WebSocket
- The client then begins streaming
StreamingPayload
messages - The server processes the stream and may return a final result
Summary
Designing streaming endpoints in Goa is straightforward with the
StreamingResult
and StreamingPayload
DSLs. By defining the type of data
to be streamed within your service methods, you can leverage Goa’s powerful code
generation to handle the underlying transport-specific streaming logic. This
ensures that your streaming endpoints are robust, efficient, and maintainable.