Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions fern/products/api-def/ferndef/endpoints/sse.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,28 @@ types:

Generated SDKs expose each event's [metadata — event ID, event type, and retry interval](/learn/sdks/deep-dives/sse-metadata) to your end users.

### Resumable streams

Set `resumable: true` on `response-stream` to opt an SSE endpoint into [automatic reconnection](/learn/sdks/deep-dives/sse-metadata#automatic-reconnection). When the connection drops mid-stream, the generated SDK reconnects and resends the last event ID in the `Last-Event-ID` header, so a server that supports that header resumes where the stream left off.

```yaml title="chat.yml" {10}
service:
base-path: /chat
endpoints:
stream:
method: POST
path: ""
response-stream:
type: Chat
format: sse
resumable: true

types:
Chat:
properties:
text: string
```

## `Stream` parameter

It has become common practice for endpoints to have a `stream` parameter that
Expand Down
32 changes: 32 additions & 0 deletions fern/products/api-def/openapi/endpoints/sse.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,38 @@ paths:
# ... responses and schemas
```

### Resumable streams

Set `resumable: true` to opt an SSE endpoint into [automatic reconnection](/learn/sdks/deep-dives/sse-metadata#automatic-reconnection). When the connection drops mid-stream, the generated SDK reconnects and resends the last event ID in the `Last-Event-ID` header, so a server that supports that header resumes where the stream left off.

```yaml title="openapi.yml" {4-7}
paths:
/logs:
post:
x-fern-streaming:
format: sse
terminator: "[DONE]"
resumable: true
# ... responses and schemas
```

Configure a [`terminator`](#terminator-message) alongside `resumable`. The terminator marks a stream as complete, letting the SDK distinguish a finished stream from a dropped connection so it reconnects only on genuine drops.

`resumable` is inheritable. Set it at the document level to apply to every SSE endpoint, and override it on individual operations:

```yaml title="openapi.yml"
x-fern-streaming:
resumable: true # applies to all SSE endpoints

paths:
/logs:
post:
x-fern-streaming:
format: sse
resumable: false # overrides the document default
# ... responses and schemas
```

## `Stream` parameter

It has become common practice for endpoints to have a `stream` parameter that
Expand Down
2 changes: 1 addition & 1 deletion fern/products/sdks/capabilities.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ Fern SDKs include the following capabilities:
- **Retries with backoff**: Automatically retry failed requests with exponential backoff. [Learn more](/sdks/deep-dives/retries-with-backoff)
- **Webhook signature verification**: Verify the signature of incoming webhook requests. [Learn more](/learn/sdks/deep-dives/webhook-signature-verification)
- **Idempotency headers**: Built-in protection against duplicate submissions. [Learn more](/sdks/deep-dives/idempotency)
- **Server-sent events**: Stream JSON data from your server to your client, with opt-in access to [SSE metadata](/learn/sdks/deep-dives/sse-metadata) (event ID, event type, retry). [Learn more](/learn/sdks/deep-dives/sse-metadata)
- **Server-sent events**: Stream JSON data from your server to your client, with opt-in access to [SSE metadata](/learn/sdks/deep-dives/sse-metadata) (event ID, event type, retry) and [automatic reconnection](/learn/sdks/deep-dives/sse-metadata#automatic-reconnection) for resumable endpoints. [Learn more](/learn/sdks/deep-dives/sse-metadata)
- **Testing**: Auto-generated and handwritten tests for your SDK. [Learn more](/sdks/deep-dives/testing)
- **Code snippets**: No longer depend on manually written code snippets. [Learn more](/docs/api-references/sdk-snippets)
- **Augment with custom code**: Extend the generated SDK with additional functionality. [Learn more](/sdks/overview/custom-code)
Expand Down
47 changes: 47 additions & 0 deletions fern/products/sdks/deep-dives/sse-metadata.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,50 @@ Each event exposes the parsed data alongside its protocol fields. Default iterat
## Stream resumption

The event ID is useful for resuming a stream via the standard `Last-Event-ID` header: store the last received ID as you iterate, then pass it back to the server on reconnection. This requires server-side support for the `Last-Event-ID` header.

## Automatic reconnection

Mark an SSE endpoint [`resumable`](/learn/api-definitions/openapi/endpoints/sse#resumable-streams) in your API definition (`x-fern-streaming.resumable: true` in OpenAPI, or `response-stream.resumable: true` in a Fern Definition) to have the SDK handle resumption for you. On a mid-stream drop, the SDK reconnects transparently, resending the last dispatched event ID in the `Last-Event-ID` header so iteration continues without gaps. No manual event-ID tracking is required.

Reconnection honors the server's `retry:` directive for the reconnect delay, falling back to a 1-second default and capped at 30 seconds. The SDK retries up to 5 consecutive times by default; the counter resets whenever an event is received. Configure a [terminator](/learn/api-definitions/openapi/endpoints/sse#terminator-message) on the endpoint so the SDK can tell a completed stream from a dropped connection.

Both the attempt cap and an on/off toggle are configurable per client and per request:

<CodeBlocks>
<CodeBlock title="TypeScript">
```ts {2}
const stream = await client.plants.stream({ query: "fern" }, {
stream: { reconnectionEnabled: true, maxReconnectionAttempts: 3 }
});
```
</CodeBlock>
<CodeBlock title="Python">
```python {3-4}
stream = client.plants.stream(
query="fern",
stream_reconnection_enabled=True,
max_stream_reconnection_attempts=3,
)
```
</CodeBlock>
<CodeBlock title="Go">
```go {4}
stream := client.Plants.Stream(
ctx,
&PlantRequest{Query: "fern"},
option.WithMaxStreamReconnectAttempts(3), // or option.WithoutStreamReconnection()
)
```
</CodeBlock>
<CodeBlock title="C#">
```csharp {2}
var stream = await client.Plants.StreamAsync(new PlantRequest { Query = "fern" }, new RequestOptions {
MaxStreamReconnectAttempts = 3 // set DisableStreamReconnection = true to turn off
});
```
</CodeBlock>
</CodeBlocks>

<Note>
Automatic reconnection requires TypeScript SDK generator version 3.77.0+, Python 5.15.0+, Go 1.42.0+, or C# 2.71.0+.
</Note>
Loading