I want to send SSE from FastAPI with different events on a single stream and receive them using HTMX-sse in the browser.
The recommended way is to use sse-starlette.
Full working example at https://devdojo.com/bobbyiliev/how-to-use-server-sent-events-sse-with-fastapi
Simplified here
import asyncio
import uvicorn
from fastapi import FastAPI, Request
app = FastAPI()
from sse_starlette.sse import EventSourceResponse
STREAM_DELAY = 1 # second
@app.get('/stream')
async def message_stream(request: Request):
cnt = 0
async def event_generator():
while True:
# If client closes connection, stop sending events
if await request.is_disconnected():
break
cnt += 1
# note: I would like to define the event name here, too
yield f"I am the data part {cnt}\nand i am multiline"
await asyncio.sleep(STREAM_DELAY)
return EventSourceResponse(event_generator())
This code works fine with one problem.
The EventSourceResponse
or event_generator
function not allow to define the
event needed by ServerSentEvent further down the code.
The SSE content messages pushed should look like this:
event: EventName
data: The data to push
The event name is optional.
Sending an event name allows the receiver to decide what to do with the message. (I'm using HTMX https://htmx.org/attributes/hx-sse/ to receive the messages.)
The content sent by this example is
data: I am the data part 1
data: and i am multiline
data: I am the data part 2
data: and i am multiline
data: I am the data part 3
data: and i am multiline
data: I am the data part 4
data: and i am multiline
...
And the data:
prefix is added to each content message. I can't remove it.
Also I can't add line breaks, each line break creates a new data:
prefix.
I would like to send different events on the same stream. The data part need to allow line breaks.
My data should look like this:
event: main
data: I am the data part 1\nand I am multiline
event: subform
data: I am the data part 2\nand I am multiline
event: main
data: I am the data part 3\nand I am multiline
event: subform
data: I am the data part 4\nand I am multiline
How can I get the EventSourceResponse
to stop adding a data:
prefix,
so that the generator can yield both prefix and context. And send multiline data in one event block?
I found the documentation regarding sse-starlette
a bit lacking, but from the examples provided I figured out the following.
To set the event type you have two options:
async def gen():
# Either a dict
yield {'event': 'EventName', 'data': 'Event Payload'}
# Or a ServerSentEvent
yield ServerSentEvent('Event Payload', event='EventName')
I'm not sure it's possible to directly send multiline data in a SSE, so I suggest encoding the data. Seems like htmx can accept plain HTML from a SSE, so you can encode your newlines as <br>
or similar. Otherwise you could attempt to encode the payload in JSON, but you'd have to decode it on the client again.