Skip to content
2 changes: 2 additions & 0 deletions docs/en/tutorial/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ The tutorial is divided into several sections:
- [Middleware](middleware/index.md) - Global request logic
- [Security](security/index.md) - Built-in protection
- [GraphQL](graphql/index.md) - GraphQL support
- [SSE](sse/index.md) - Server-Sent Events
- [WebSocket](websocket/index.md) - WebSocket connections
- [OpenAPI](openapi/index.md) - Swagger UI

## How to Use
Expand Down
183 changes: 183 additions & 0 deletions docs/en/tutorial/sse/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Server-Sent Events (SSE)

FastHTTP supports **Server-Sent Events (SSE)** — a one-way push protocol over plain HTTP where the server streams events to the client as they happen.

SSE routes are defined with the `@app.sse()` decorator. The handler is called once per event and receives an [`SSEEvent`][sse-event] object.

---

## Quick Start

Connect to a public SSE stream and print each event:

```python
from fasthttp import FastHTTP, SSEEvent

app = FastHTTP()


@app.sse(url="https://stream.wikimedia.org/v2/stream/recentchange")
async def watch_wikipedia(event: SSEEvent) -> None:
import json
data = json.loads(event.data)
print(f"[{event.event}] {data['title']}")


if __name__ == "__main__":
app.run()
```

Run it:

```bash
python main.py
```

Output:

```
INFO | fasthttp | FastHTTP started
INFO | fasthttp | Running 1 routes
INFO | fasthttp | SSE streams: 1
INFO | fasthttp | SSE connecting: https://stream.wikimedia.org/v2/stream/recentchange
INFO | fasthttp | SSE connected: https://stream.wikimedia.org/v2/stream/recentchange
[message] Frank Sinatra
[message] Python (programming language)
[message] List of mountains by elevation
^C INFO | fasthttp | Interrupted by user
```

Press **Ctrl+C** to stop.

---

## `@app.sse()` Signature

```python
@app.sse(
url: str,
*,
headers: dict[str, str] | None = None,
reconnect: bool = False,
max_retries: int = 0,
tags: list[str] | None = None,
)
```

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `url` | `str` | — | SSE endpoint URL (`http://` or `https://`). |
| `headers` | `dict[str, str]` | `None` | Extra headers sent with the connection request. |
| `reconnect` | `bool` | `False` | Automatically reconnect when the connection drops. |
| `max_retries` | `int` | `0` | Max reconnect attempts (ignored when `reconnect=False`). `-1` = unlimited. |
| `tags` | `list[str]` | `None` | Tags for grouping and filtering routes. |

---

## The `SSEEvent` Object

Every event carries these fields:

| Field | Type | Description |
|-------|------|-------------|
| `event` | `str` | Event type from the server (`"message"` by default). |
| `data` | `str` | The event payload as a string. |
| `id` | `str \| None` | Event ID used for reconnection tracking (`Last-Event-ID`). |
| `retry` | `int \| None` | Reconnection time in milliseconds suggested by the server. |

```python
@app.sse(url="https://api.example.com/events")
async def handle(event: SSEEvent) -> None:
print(f"Type: {event.event}") # "update", "alert", ...
print(f"Data: {event.data}") # the payload
print(f"ID: {event.id}") # for reconnection
```

---

## Custom Headers

Pass `headers` for authentication or custom User-Agent:

```python
@app.sse(
url="https://api.example.com/events",
headers={
"Authorization": "Bearer your-token",
"User-Agent": "MyApp/1.0",
},
)
async def handle(event: SSEEvent) -> None:
print(event.data)
```

---

## Auto-Reconnect

When `reconnect=True`, FastHTTP reconnects with exponential backoff (`2^attempt` seconds, capped at 30s) if the connection drops. The `Last-Event-ID` header is sent automatically so the server can resume from where you left off.

```python
@app.sse(
url="https://api.example.com/events",
reconnect=True,
max_retries=10,
)
async def handle(event: SSEEvent) -> None:
print(event.data)
```

Set `max_retries=-1` for unlimited reconnection attempts.

---

## Running with HTTP Routes

SSE streams run alongside HTTP requests and WebSocket connections. HTTP requests complete first, then the app stays alive until all SSE streams are done or interrupted.

```python
from fasthttp import FastHTTP, SSEEvent
from fasthttp.response import Response

app = FastHTTP()


@app.get(url="https://api.example.com/status")
async def health(resp: Response) -> dict:
return resp.json()


@app.sse(url="https://stream.wikimedia.org/v2/stream/recentchange")
async def watch(event: SSEEvent) -> None:
print(event.data)


if __name__ == "__main__":
app.run() # HTTP completes first, then SSE stays alive until Ctrl+C
```

---

## Local Development

For testing without a remote server, create a local SSE server. See the [`examples/sse/local_server.py`](https://github.com/ndugram/fasthttp/blob/master/examples/sse/local_server.py) for a complete self-contained example.

```bash
python examples/sse/local_server.py
```

---

## SSE vs WebSocket

| Feature | SSE | WebSocket |
|---------|-----|-----------|
| Direction | Server → Client | Bidirectional |
| Protocol | Plain HTTP | WS / WSS |
| Message format | Text only (`data:` lines) | Text + Binary |
| Auto-reconnect | Built-in (browsers) / via `reconnect=True` | Manual |
| Use case | Live feeds, notifications, AI token streaming | Chat, gaming, real-time collaboration |

Use SSE when you only need to **receive** events from a server. Use WebSocket when you also need to **send** data back.

[sse-event]: ../../reference/fasthttp.md#fasthttp.sse.SSEEvent
2 changes: 2 additions & 0 deletions docs/ru/tutorial/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
- [Middleware](middleware/index.md) - Глобальная логика запросов
- [Безопасность](security/index.md) - Встроенная защита
- [GraphQL](graphql/index.md) - Поддержка GraphQL
- [SSE](sse/index.md) - Server-Sent Events
- [WebSocket](websocket/index.md) - WebSocket соединения
- [OpenAPI](openapi/index.md) - Swagger UI

## Как использовать
Expand Down
183 changes: 183 additions & 0 deletions docs/ru/tutorial/sse/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
# Server-Sent Events (SSE)

FastHTTP поддерживает **Server-Sent Events (SSE)** — однонаправленный протокол «толкающих» уведомлений поверх обычного HTTP, где сервер стримит события по мере их возникновения.

SSE-роуты определяются через декоратор `@app.sse()`. Хэндлер вызывается на каждое событие и получает объект [`SSEEvent`][sse-event].

---

## Быстрый старт

Подключитесь к публичному SSE-потоку Wikimedia и печатайте каждое событие:

```python
from fasthttp import FastHTTP, SSEEvent

app = FastHTTP()


@app.sse(url="https://stream.wikimedia.org/v2/stream/recentchange")
async def watch_wikipedia(event: SSEEvent) -> None:
import json
data = json.loads(event.data)
print(f"[{event.event}] {data['title']}")


if __name__ == "__main__":
app.run()
```

Запустите:

```bash
python main.py
```

Вывод:

```
INFO | fasthttp | FastHTTP started
INFO | fasthttp | Running 1 routes
INFO | fasthttp | SSE streams: 1
INFO | fasthttp | SSE connecting: https://stream.wikimedia.org/v2/stream/recentchange
INFO | fasthttp | SSE connected: https://stream.wikimedia.org/v2/stream/recentchange
[message] Frank Sinatra
[message] Python (programming language)
[message] List of mountains by elevation
^C INFO | fasthttp | Interrupted by user
```

Нажмите **Ctrl+C** для остановки.

---

## Сигнатура `@app.sse()`

```python
@app.sse(
url: str,
*,
headers: dict[str, str] | None = None,
reconnect: bool = False,
max_retries: int = 0,
tags: list[str] | None = None,
)
```

| Параметр | Тип | По умолчанию | Описание |
|----------|-----|--------------|----------|
| `url` | `str` | — | URL SSE-эндпоинта (`http://` или `https://`). |
| `headers` | `dict[str, str]` | `None` | Дополнительные заголовки запроса. |
| `reconnect` | `bool` | `False` | Автопереподключение при разрыве. |
| `max_retries` | `int` | `0` | Максимум попыток переподключения. `-1` = безлимит. |
| `tags` | `list[str]` | `None` | Теги для группировки и фильтрации. |

---

## Объект `SSEEvent`

Каждое событие содержит:

| Поле | Тип | Описание |
|------|-----|----------|
| `event` | `str` | Тип события (по умолчанию `"message"`). |
| `data` | `str` | Тело события в виде строки. |
| `id` | `str \| None` | ID события для отслеживания переподключения (`Last-Event-ID`). |
| `retry` | `int \| None` | Время переподключения в мс (от сервера). |

```python
@app.sse(url="https://api.example.com/events")
async def handle(event: SSEEvent) -> None:
print(f"Тип: {event.event}") # "update", "alert", ...
print(f"Данные: {event.data}") # payload
print(f"ID: {event.id}") # для переподключения
```

---

## Пользовательские заголовки

Передавайте `headers` для аутентификации или кастомного User-Agent:

```python
@app.sse(
url="https://api.example.com/events",
headers={
"Authorization": "Bearer your-token",
"User-Agent": "MyApp/1.0",
},
)
async def handle(event: SSEEvent) -> None:
print(event.data)
```

---

## Автопереподключение

При `reconnect=True` FastHTTP переподключается с экспоненциальной задержкой (`2^attempt` секунд, не более 30с). Заголовок `Last-Event-ID` отправляется автоматически — сервер может продолжить с последнего полученного события.

```python
@app.sse(
url="https://api.example.com/events",
reconnect=True,
max_retries=10,
)
async def handle(event: SSEEvent) -> None:
print(event.data)
```

Установите `max_retries=-1` для безлимитных попыток.

---

## Совместная работа с HTTP

SSE-потоки выполняются вместе с HTTP-запросами и WebSocket-соединениями. HTTP завершается первым, затем приложение остаётся активным до завершения SSE-потоков или Ctrl+C.

```python
from fasthttp import FastHTTP, SSEEvent
from fasthttp.response import Response

app = FastHTTP()


@app.get(url="https://api.example.com/status")
async def health(resp: Response) -> dict:
return resp.json()


@app.sse(url="https://stream.wikimedia.org/v2/stream/recentchange")
async def watch(event: SSEEvent) -> None:
print(event.data)


if __name__ == "__main__":
app.run() # HTTP выполняется первым, SSE остаётся активным до Ctrl+C
```

---

## Локальная разработка

Для тестирования без удалённого сервера запустите встроенный пример с локальным SSE-сервером:

```bash
python examples/sse/local_server.py
```

---

## SSE vs WebSocket

| Возможность | SSE | WebSocket |
|-------------|-----|-----------|
| Направление | Сервер → Клиент | Двустороннее |
| Протокол | Обычный HTTP | WS / WSS |
| Формат | Только текст (`data:` строки) | Текст + Бинарные |
| Автопереподключение | Встроено | Вручную |
| Сценарии | Ленты событий, уведомления, AI-токены | Чат, игры, совместная работа |

Используйте SSE если нужно только **получать** события от сервера. Используйте WebSocket если нужно ещё и **отправлять** данные обратно.

[sse-event]: ../../reference/fasthttp.md#fasthttp.sse.SSEEvent
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ All examples are tested and working.
- **error** — automatic error handling and testing various error scenarios
- **middleware** - test middleware for FastHTTP
- **events** - event hooks for request/response lifecycle
- **sse** - Server-Sent Events (real-time event streams)
- **pydantic** - pydantic response validation examples
- **http2** - http2 test for fasthttp
- **tags** - grouping and filtering requests by tags
Expand Down
Empty file added examples/sse/__init__.py
Empty file.
Loading
Loading