Last active
May 12, 2025 03:52
-
-
Save bouroo/4b91e535a7f036cb8ab2ba4cb16ae88e to your computer and use it in GitHub Desktop.
example for cloudflare worker server-sent events
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Cloudflare Worker for Server-Sent Events (SSE) | |
* | |
* This worker demonstrates how to set up an SSE endpoint | |
* that sends an initial message and then periodic updates. | |
*/ | |
// Listen for incoming requests | |
addEventListener('fetch', event => { | |
event.respondWith(handleRequest(event.request)); | |
}); | |
/** | |
* Handles incoming requests and initiates the SSE stream. | |
* @param {Request} request The incoming request. | |
* @returns {Response} The SSE response. | |
*/ | |
async function handleRequest(request) { | |
// Create a stream to write the SSE messages. | |
// TransformStream acts as a buffer between the writer and the reader. | |
const { readable, writable } = new TransformStream(); | |
// Set necessary headers for SSE and CORS. | |
const headers = new Headers({ | |
'Content-Type': 'text/event-stream', | |
'Cache-Control': 'no-cache', // Important for SSE to prevent buffering | |
'Connection': 'keep-alive', // Keep the connection open | |
'Access-Control-Allow-Origin': '*', // Allow requests from any origin | |
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept', | |
}); | |
// Asynchronously write messages to the stream. | |
// We don't wait for this to complete, allowing the Response to be returned immediately | |
// with the readable stream connected to the writable stream. | |
streamSSEMessages(writable).catch(console.error); // Log potential errors during streaming | |
// Return the response with the readable stream attached. | |
// The client will receive data as it's written to the writable stream. | |
return new Response(readable, { headers }); | |
} | |
/** | |
* Writes Server-Sent Events (SSE) messages to the provided WritableStream. | |
* @param {WritableStream} writable The stream to write messages to. | |
*/ | |
async function streamSSEMessages(writable) { | |
const writer = writable.getWriter(); | |
const encoder = new TextEncoder(); // Reuse TextEncoder for efficiency | |
let count = 0; | |
const intervalDuration = 5000; // 5 seconds between messages | |
/** | |
* Helper function to format and write a single SSE message block. | |
* @param {string|number} id The event ID. | |
* @param {string} [eventType] The event type (optional). | |
* @param {any} data The data payload (will be JSON stringified). | |
*/ | |
const writeSSE = async (id, eventType, data) => { | |
let message = `id: ${id}\n`; | |
if (eventType) { | |
message += `event: ${eventType}\n`; | |
} | |
message += `data: ${JSON.stringify(data)}\n\n`; // Data field followed by double newline | |
await writer.write(encoder.encode(message)); | |
}; | |
// Send the initial connection message. | |
const initialData = { status: true, text: "Hello to SSE message", time: new Date().toISOString() }; | |
await writeSSE(`id-${++count}`, "userConnected", initialData); | |
// Set up an interval to send periodic messages. | |
const intervalId = setInterval(async () => { | |
const periodicData = { status: true, text: `Repeat message: ${count + 1}`, time: new Date().toISOString() }; | |
// Use a try-catch inside the interval to handle potential errors during writing | |
// without stopping the interval immediately (though stream closure handles cleanup). | |
try { | |
await writeSSE(`id-${++count}`, "userMessage", periodicData); | |
} catch (error) { | |
console.error("Error writing to stream in interval:", error); | |
// The stream should ideally close on error, triggering the writable.closed handler. | |
} | |
}, intervalDuration); | |
// Clean up the interval and release the writer lock when the stream closes | |
// (e.g., client disconnects). | |
writable.closed | |
.then(() => { | |
clearInterval(intervalId); | |
writer.releaseLock(); // Release the lock acquired by getWriter() | |
console.log("SSE stream closed and interval cleared."); | |
}) | |
.catch(err => { | |
console.error("SSE stream closed with error:", err); | |
clearInterval(intervalId); | |
writer.releaseLock(); // Ensure lock is released even if stream closes with error | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment