Unit 10.02: Streaming to a browser without lying
Streaming to a browser is easy. Streaming honestly is the part that needs thought.
Chunks out, and what you must not do with them
A streaming endpoint consumed by the test client.
The code reassembles the chunks.
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from fastapi.testclient import TestClient
app = FastAPI()
CHUNKS = ["Refunds ", "are ", "allowed ", "within 7 days."]
@app.get("/stream")
def stream():
def generate():
for chunk in CHUNKS:
yield chunk
return StreamingResponse(generate(), media_type="text/plain")
client = TestClient(app)
with client.stream("GET", "/stream") as response:
received = [c for c in response.iter_text()]
print("received:", received)
print("reassembled:", "".join(received))
print("""
Two rules for streaming. Never present partial content as a final answer -- if
validation fails after the stream, you must visibly replace what the user is
already reading.
And do not stream a draft that still has to pass a human gate. Text in the
answer position gets acted on before anyone approves it.
""")
Two rules follow. If validation fails after the stream, you have to visibly replace text the user is already reading - so anything that must pass a check should not be streamed as an answer.
And a draft still awaiting a human gate must not be streamed into the answer position. Text there gets acted on before anyone approves it, which quietly defeats the gate.
The mistake this prevents
The mistake is streaming because it feels responsive, without asking what happens when the content turns out to be unusable. Perceived speed is not worth showing a user something you may have to retract.
Takeaway
Stream progress freely; stream content only when it does not need to pass a later check. A draft awaiting approval does not belong in the answer position.
