Skip to main content 首页 创作者 fabioc-aloha chesscoach sse-streaming
sse-streaming POST-based Server-Sent Events streaming for Azure Functions — HTTP streaming, chunked response parsing, reconnection
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/fabioc-aloha/ChessCoach --skill sse-streaming命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... type skill lifecycle stable inheritance inheritable name sse-streaming description POST-based Server-Sent Events streaming for Azure Functions — HTTP streaming, chunked response parsing, reconnection tier extended applyTo **/*sse*,**/*streaming*,**/*server-sent*,**/*real-time* metadata {"version":"1.0.0","category":"web-development","tags":["sse","streaming","azure-functions","http-streaming","readablestream","real-time"],"activation":{"triggers":["SSE","streaming","server-sent events","HTTP streaming","ReadableStream","chunked response","real-time"],"context":"Working on real-time streaming from Azure Functions to client"},"prerequisites":[]} currency 2026-04-22T00:00:00.000Z
SSE Streaming
POST-based Server-Sent Events pattern for Azure Functions. Solves the gap where native EventSource (GET-only) can't send request bodies, and Azure Static Web Apps don't proxy WebSocket to the API layer.
Why POST-Based SSE
Option SWA API Support? POST body? Limitation WebSocket No N/A SWA doesn't proxy WebSocket to Functions EventSource Yes GET only Can't send context in request body POST + ReadableStream Yes Yes Recommended pattern
Azure Functions Setup
Enable HTTP Streaming
{
"version" : "2.0" ,
"extensions" : {
"http" : {
"enableHttpStream" : true
}
}
}
Streaming Function Pattern
import { app, HttpRequest , HttpResponseInit , InvocationContext } from '@azure/functions' ;
app.http ('stream-response' , {
methods : ['POST' ],
authLevel : 'anonymous' ,
: ,
: ( : , : ): < > => {
payload = req. ();
stream = ({
( ) {
{
aiStream = (payload);
( chunk aiStream) {
sseData = ;
controller. ( (). (sseData));
}
controller. (
(). ( )
);
} (error) {
controller. (
(). ( )
);
} {
controller. ();
}
},
});
{
: ,
: {
: ,
: ,
: ,
: ,
},
: stream,
};
},
});
route
'stream'
handler
async
req
HttpRequest
context
InvocationContext
Promise
HttpResponseInit
const
await
json
const
new
ReadableStream
async
start
controller
try
const
await
getAIStream
for
await
const
of
const
`data: ${JSON .stringify({ text: chunk, done: false })} \n\n`
enqueue
new
TextEncoder
encode
enqueue
new
TextEncoder
encode
`data: ${JSON .stringify({ done: true })} \n\n`
catch
enqueue
new
TextEncoder
encode
`data: ${JSON .stringify({ error: 'Stream failed' , done: true })} \n\n`
finally
close
return
status
200
headers
'Content-Type'
'text/event-stream'
'Cache-Control'
'no-cache'
'Connection'
'keep-alive'
'X-Accel-Buffering'
'no'
body
Client Consumption
POST + ReadableStream Pattern interface SSEMessage {
text ?: string ;
done : boolean ;
error ?: string ;
}
async function streamResponse (
payload : unknown ,
onChunk : (text: string ) => void ,
onComplete : () => void ,
onError : (error: string ) => void ,
): Promise <void > {
const response = await fetch ('/api/stream' , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' },
body : JSON .stringify (payload),
});
if (!response.ok ) {
onError (`HTTP ${response.status} : ${response.statusText} ` );
return ;
}
const reader = response.body !.getReader ();
const decoder = new TextDecoder ();
let buffer = '' ;
while (true ) {
const { done, value } = await reader.read ();
if (done) break ;
buffer += decoder.decode (value, { stream : true });
const messages = buffer.split ('\n\n' );
buffer = messages.pop () || '' ;
for (const msg of messages) {
if (!msg.startsWith ('data: ' )) continue ;
const json = msg.slice (6 );
try {
const parsed : SSEMessage = JSON .parse (json);
if (parsed.error ) { onError (parsed.error ); return ; }
if (parsed.text ) { onChunk (parsed.text ); }
if (parsed.done ) { onComplete (); return ; }
} catch {
}
}
}
onComplete ();
}
React Hook function useStreamedResponse ( ) {
const [text, setText] = useState ('' );
const [isStreaming, setIsStreaming] = useState (false );
const [error, setError] = useState<string | null >(null );
const stream = useCallback (async (payload : unknown ) => {
setText ('' );
setIsStreaming (true );
setError (null );
await streamResponse (
payload,
(chunk ) => setText (prev => prev + chunk),
() => setIsStreaming (false ),
(err ) => { setError (err); setIsStreaming (false ); },
);
}, []);
return { text, isStreaming, error, stream };
}
Cancellation with AbortController async function streamWithAbort (
payload : unknown ,
signal : AbortSignal ,
onChunk : (text: string ) => void ,
): Promise <void > {
const response = await fetch ('/api/stream' , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/json' },
body : JSON .stringify (payload),
signal,
});
const reader = response.body !.getReader ();
signal.addEventListener ('abort' , () => reader.cancel ());
}
const controller = new AbortController ();
streamWithAbort (payload, controller.signal , onChunk);
controller.abort ();
SWA Timeout Handling Azure Static Web Apps has a 45-second API timeout . For long operations, use multi-phase streaming:
app.http ('long-operation' , {
methods : ['POST' ],
handler : async (req, context) => {
const stream = new ReadableStream ({
async start (controller ) {
const send = (data : object ) =>
controller.enqueue (new TextEncoder ().encode (`data: ${JSON .stringify(data)} \n\n` ));
send ({ phase : 'step-1' , progress : 0.1 });
const result1 = await doStep1 ();
send ({ phase : 'step-2' , progress : 0.5 });
const result2 = await doStep2 (result1);
send ({ phase : 'complete' , progress : 1.0 , result : result2 });
controller.close ();
},
});
return { body : stream, headers : { 'Content-Type' : 'text/event-stream' } };
},
});
Error Recovery
Retry Strategy async function streamWithRetry (
payload : unknown ,
onChunk : (text: string ) => void ,
maxRetries : number = 2 ,
): Promise <void > {
let lastError : string | undefined ;
for (let attempt = 0 ; attempt <= maxRetries; attempt++) {
try {
await streamResponse (payload, onChunk, () => {}, (err ) => { throw new Error (err); });
return ;
} catch (error) {
lastError = error instanceof Error ? error.message : 'Unknown error' ;
if (attempt < maxRetries) {
await new Promise (resolve => setTimeout (resolve, 1000 * (attempt + 1 )));
}
}
}
throw new Error (`Stream failed after ${maxRetries + 1 } attempts: ${lastError} ` );
}
Activation Patterns Trigger Response "SSE", "streaming", "server-sent events" Full skill activation "Azure Functions stream", "HTTP streaming" Azure Functions Setup section "ReadableStream", "chunked response" Client Consumption section "abort", "cancel stream" Cancellation section "SWA timeout", "45 second" SWA Timeout Handling section