Coming From

If you're coming from File Handling, you'll find streaming provides a more efficient way to handle large files.

Streaming Responses

Like Batman's gadgets streaming from the Batcave to his utility belt, Robyn provides built-in support for streaming responses. This allows you to send data in chunks, perfect for large files, real-time updates, and server-sent events.

Streaming responses are perfect for handling large datasets or real-time updates without consuming excessive memory.

Response

When the Bat-Signal needs to stream continuously through the night sky, you'll want to use a generator or iterator as the description parameter:

Server

from robyn import Response

@app.get("/bat-signal")
async def stream_signal():
    async def signal_generator():
        while True:
            yield b"Bat-Signal Active\n"
            await asyncio.sleep(1)
    
    return Response(
        status_code=200,
        headers={"Content-Type": "text/plain"},
        description=signal_generator()
    )

Client

curl http://localhost:8000/bat-signal

Parameters

NameTypeDescriptionDefault
status_codeintResponse status code200
headersDict[str, str]Response headersNone
descriptionUnion[str, bytes, Generator, AsyncGenerator]Content to streamNone

Supported Types

Like Batman's versatile arsenal, the streaming response system supports multiple data types:

Binary

# Raw binary data (like Batcomputer logs)
yield b"Batcomputer Log Entry\n"

Text

# Text messages (like Alfred's updates)
yield "Master Wayne, your tea is ready\n".encode()

Numbers

# Numbers (like Batmobile telemetry)
yield str(speed).encode()

JSON

# JSON data (like Gotham City surveillance)
yield json.dumps({"location": "Crime Alley"}).encode()

Server-Sent Events

For real-time updates from the Batcomputer:

Server

@app.get("/batcomputer/events")
async def batcomputer_feed():
    async def event_generator():
        while True:
            data = {
                "time": time.time(),
                "alerts": get_gotham_alerts()
            }
            yield f"data: {json.dumps(data)}\n\n".encode()
            await asyncio.sleep(1)
    
    return Response(
        status_code=200,
        headers={
            "Content-Type": "text/event-stream",
            "Cache-Control": "no-cache",
            "Connection": "keep-alive"
        },
        description=event_generator()
    )

Client

const evtSource = new EventSource("/batcomputer/events");
evtSource.onmessage = (event) => {
    console.log(JSON.parse(event.data));
};

File Downloads

For streaming large files from the Batcomputer archives:

Server

@app.get("/batcomputer/files")
async def download_files():
    async def file_generator():
        chunk_size = 8192  # Size of a Batarang
        with open("case_files.dat", "rb") as f:
            while chunk := f.read(chunk_size):
                yield chunk
    
    return Response(
        status_code=200,
        headers={
            "Content-Type": "application/octet-stream",
            "Content-Disposition": "attachment; filename=evidence.dat"
        },
        description=file_generator()
    )

Client

curl -O http://localhost:8000/batcomputer/files

Common Headers

Plain Text

headers = {"Content-Type": "text/plain"}

Server-Sent Events

headers = {
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
    "Connection": "keep-alive"
}

File Downloads

headers = {
    "Content-Type": "application/octet-stream",
    "Content-Disposition": "attachment; filename=file.dat"
}

Error Handling

Even Batman needs contingency plans:

Always handle errors gracefully in your streaming responses to prevent connection hangs.

async def generator():
    try:
        for item in evidence_items:
            yield process(item)
    except Exception as e:
        yield f"Alert: Batcomputer Error - {str(e)}".encode()
        return

Testing

Test your streaming responses like Batman testing his equipment:

@pytest.mark.asyncio
async def test_bat_signal():
    async with app.test_client() as client:
        response = await client.get("/bat-signal")
        signals = []
        async for signal in response.content:
            signals.append(signal)
        assert len(signals) > 0

Best Practices

Encode Data

Always encode strings to bytes (like encrypting Bat-communications)

Chunk Size

Use appropriate chunk sizes (8KB-64KB for efficient data transfer)

Resource Management

Clean up resources (like Batman cleaning up Gotham)

Memory Usage

Don't accumulate data in memory (keep the Batcomputer running smoothly)

Timeouts

Implement timeouts (even Batman needs sleep)

What's Next?

Now that you've mastered streaming, you might want to explore:

  • WebSockets - For real-time bidirectional communication
  • Scaling - Scale your streaming applications across multiple cores