Skip to content

Latest commit

 

History

History
137 lines (93 loc) · 4.7 KB

File metadata and controls

137 lines (93 loc) · 4.7 KB

Subscription Protocol Specification (SSE)

Overview

Subscriptions provide real-time streaming data from server to client using Server-Sent Events (SSE). A subscription is a named stream endpoint that accepts input parameters and yields a sequence of typed values.

Endpoint

GET /_seam/procedure/{subscriptionName}?input={json}
Parameter Location Description
subscriptionName path Name of the subscription (from manifest)
input query URL-encoded JSON matching the subscription's input schema

When input is omitted, the server defaults to {}.

Response

The server responds with Content-Type: text/event-stream and streams SSE events. The connection stays open until the stream completes, an error occurs, or the client disconnects.

Event Types

data

A single value from the subscription stream.

id: 0
event: data
data: {"n":1}

All subscription SSE events carry an incrementing id field (starting from 0). This is consistent across all backends (TS, Rust, Go). The data payload is JSON matching the subscription's output schema.

error

An error occurred during the subscription.

event: error
data: {"code":"VALIDATION_ERROR","message":"Input validation failed","transient":false}

The transient field indicates whether the error is temporary and the client may retry. Defaults to false.

Error codes reuse the same set as procedure errors. See Error Codes for the full list.

After an error event the server closes the stream.

complete

The subscription stream has finished normally.

event: complete
data: {}

After a complete event the server closes the connection.

Manifest Integration

Subscriptions appear in the procedure manifest alongside regular procedures. They are distinguished by the kind field:

{
	"version": 2,
	"context": {},
	"procedures": {
		"greet": {
			"kind": "query",
			"input": { "properties": { "name": { "type": "string" } } },
			"output": { "properties": { "message": { "type": "string" } } }
		},
		"onCount": {
			"kind": "subscription",
			"input": { "properties": { "max": { "type": "int32" } } },
			"output": { "properties": { "n": { "type": "int32" } } }
		}
	},
	"transportDefaults": {}
}

The kind field defaults to "query" when absent (backward compatible). The "type" alias is accepted for v1 compatibility.

Subscription vs Stream: both use SSE, but they differ in direction and schema:

  • Subscription (GET, output): server pushes events without client input beyond the initial request.
  • Stream (POST, chunkOutput): client sends input via POST body, server responds with SSE events carrying incrementing id fields.

See Procedure Manifest for full stream details.

WebSocket Alternative

Channel subscriptions ({channel}.events) can also be consumed over WebSocket, which enables bidirectional communication -- the client can invoke channel commands over the same connection. See Channel Protocol for the WebSocket wire format.

When a WebSocket Upgrade header is present on a channel subscription request, the server upgrades the connection instead of starting an SSE stream.

Client Disconnect

When the client closes the SSE connection (e.g. by calling EventSource.close() or navigating away), the server should detect the broken pipe and stop producing values. Cleanup logic in subscription handlers should release resources promptly.

Resumption

The browser EventSource API automatically sends a Last-Event-ID header on reconnect, containing the id of the last received event. The server passes this value to the subscription handler:

  • TypeScript: handler({ input, ctx, lastEventId })lastEventId is string | undefined
  • Rust: SubscriptionParams { input, ctx, last_event_id }last_event_id is Option<String>
  • Go: context.Value(lastEventIDKey) — extracted from the request header

Handlers can use lastEventId to skip already-sent events and resume the stream from where the client left off.

Error Handling

HTTP-level errors

If the server can detect the error before starting the SSE stream (e.g. empty subscription name, unparseable input query parameter), it returns a regular JSON error response with the appropriate HTTP status code.

Stream-level errors

If the error occurs after the SSE stream has started (e.g. the subscription name is unknown, input fails validation, or the handler throws), the server sends an error SSE event and closes the stream.