Skip to content

SSE Support in @typespec/http #2378

@bterlson

Description

@bterlson

Our HTTP library should add support for defining SSE endpoints.

SSE is a fairly simple protocol where a service responds with a content type of text/event-stream and events are encoded in plaintext. Events are separated by two line breaks. Event data is provided by lines prefixed with data:. Events can optionally have an event type, in which case the data: lines for the event are preceded by an event: line.

Clients can consume SSE Events in web browsers using the EventSource API, but naturally we want to generate clients that make this more ergonomic.

Proposal

The gist of this proposal is to enable specs like the following:

@post op createCompletionStream(): SSEStream<Completion>;

model Completion {
  text: string;
}

Or, using event types:

@post op createCompletionStream(): SSEStream<Events>;

model Completion {
  text: string;
}

@sseEventTypes union Events {
  completion: Completion
  error: string;
}

SSEStream<T, TEventEncoding = "application/json">

Defined as the following:

@sseStreamOf(T)
@sseEventContentType(TEventEncoding)
model SSEStream<T, TEventEncoding = "application/json"> {
  @header contentType: "text/event-stream";
  @body body: string;
}

This type enables emitters that don't understand SSE to still consume a vaguely appropriate shape including the proper content type and string body. If you understand SSE, the following decorators are used to determine the API shape.

SSE event payloads are just strings, but in practice they are often JSON encoded. This template allows customizing how an emitter should encode/decode the SSE event payloads. "application/json" is probably the most common so it is default, however "text/plain" would also be another common option.

@sseStreamOf(T: unknown) (internal-ish)

Indicates the payload of SSE events. If T has the @sseEventTypes decorator, it defines the event types and payloads. Otherwise, event types are not used and events all share the same provided shape.

@sseEventContentType(T: valueof string)

Customizes the content type of the event payload, enabling emitters to encode/decode different serializations.

May be applied to union variants when the union has the @sseEventTypes decorator. In such cases, it overrides the value set on the response payload (i.e. the value passed to the SSEStream template).

@sseEventTypes

Applied to a union, this decorator instructs the emitter to treat the union as defining the event types in the API.

When the a union variant is named, the name is the event type and the value is the corresponding payload type. When the union variant is anonymous, no event type is sent. This means that multiple anonymous union variants all describe possible events that may or may not be discriminated, and emitters will likely need to do reflection on the event value is to see what logical event they're dealing with.

@sseEventBody

Applied to a model property, this decorator defines what the logical body of the event is. This allows emitters limited ability to unpack event envelopes, passing only the logical event into user code. We may want to only support this at the top level of models for now.

Example usage:

@post op createCompletionStream(): SSEStream<IteratorResult<string>>;

@sseEventTypes union IteratorResult<T> {
  @sseTerminalEvent
  { done: true };

  { done: false, @sseEventBody value: T };
} 

Metadata

Metadata

Assignees

Labels

design:neededA design request has been raised that needs a proposaltriaged:core

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions