Thursday, September 25, 2025

How to have SSE based respones from FAstAPI to react front end ?

Below is the python script to do this

==========================

from fastapi import FastAPI

from fastapi.middleware.cors import CORSMiddleware

from starlette.responses import StreamingResponse

import asyncio


app = FastAPI()


# Configure CORS to allow the React app to connect

origins = [

    "http://localhost:3000",  # Default create-react-app port

]


app.add_middleware(

    CORSMiddleware,

    allow_origins=origins,

    allow_credentials=True,

    allow_methods=["*"],

    allow_headers=["*"],

)


async def stream_generator():

    """Generates a stream of data for SSE."""

    for i in range(1, 11):

        yield f"data: This is message number {i}.\n\n"

        await asyncio.sleep(1)

    

@app.get("/stream")

async def get_stream():

    """Endpoint that returns a Server-Sent Event stream."""

    return StreamingResponse(stream_generator(), media_type="text/event-stream")


Below is the app jsx file 

=================

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSE React App</title>
<script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
background-color: #f0f2f5;
}
.container {
background: #fff;
padding: 40px;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
max-width: 600px;
width: 90%;
margin-top: 40px;
}
h1 {
color: #333;
font-size: 2rem;
text-align: center;
margin-bottom: 1rem;
}
.info-text {
color: #666;
text-align: center;
margin-bottom: 2rem;
}
.output-box {
background: #fafafa;
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
min-height: 150px;
margin-top: 20px;
overflow-y: auto;
font-size: 0.9rem;
line-height: 1.6;
color: #444;
white-space: pre-wrap;
word-wrap: break-word;
}
.placeholder-text {
color: #999;
text-align: center;
font-style: italic;
padding-top: 20px;
}
.error-text {
color: #d9534f;
text-align: center;
font-weight: bold;
}
.loading-text {
color: #5bc0de;
text-align: center;
font-style: italic;
}
.action-button {
display: block;
width: 100%;
padding: 12px 24px;
margin-top: 25px;
font-size: 1.1rem;
font-weight: bold;
color: #fff;
background-color: #007bff;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s, transform 0.1s;
}
.action-button:hover:not(:disabled) {
background-color: #0056b3;
}
.action-button:active:not(:disabled) {
transform: translateY(1px);
}
.action-button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
</style>
</head>
<body>
<div id="root"></div>

<script type="text/babel">
const { useState, useEffect } = React;

function App() {
const [streamedText, setStreamedText] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);

const startStreaming = () => {
setIsLoading(true);
setError(null);
setStreamedText("");
const eventSource = new EventSource("http://localhost:8000/stream");
eventSource.onmessage = (event) => {
setStreamedText(prevText => prevText + event.data);
};
eventSource.onerror = (err) => {
console.error("EventSource failed:", err);
setError("Failed to connect to the streaming service. Please check the server.");
setIsLoading(false);
eventSource.close();
};
eventSource.onopen = () => {
console.log("Connection opened.");
// This will automatically stop the loading state when the stream closes
eventSource.addEventListener('close', () => {
setIsLoading(false);
eventSource.close();
});
};
};

return (
<div className="container">
<h1>SSE Streaming Demo</h1>
<p className="info-text">
This is a simple demo showing a React client consuming a Server-Sent Event stream from a FastAPI backend.
<br />
Make sure your backend server is running on <a href="http://localhost:8000" target="_blank" rel="noopener noreferrer">http://localhost:8000</a> before you click the button.
</p>
<div className="output-box">
{isLoading && !error && (
<p className="loading-text">Streaming response...</p>
)}
{error && <p className="error-text">{error}</p>}
{!isLoading && streamedText.length === 0 && !error && (
<p className="placeholder-text">Click the button below to start the stream.</p>
)}
<pre>{streamedText}</pre>
</div>
<button onClick={startStreaming} disabled={isLoading} className="action-button">
{isLoading ? 'Streaming...' : 'Start Streaming'}
</button>
</div>
);
}

ReactDOM.render(<App />, document.getElementById('root'));
</script>
</body>
</html>


 

No comments:

Post a Comment