axiosleo/node-koapp/koapp-sse

Claude Code and agent skill by axiosleo/node-koapp

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Stream Server-Sent Events (SSE) from a @axiosleo/koapp Koa route using the KoaSSEMiddleware. Use when pushing real-time events to a browser over HTTP, implementing progress bars, live logs, notifications, or EventSource endpoints with text/event-stream, including ctx.koa.sse.send/close/keepAlive APIs.

Install

npx vskill@latest install axiosleo/node-koapp/koapp-sse

SKILL.md

@axiosleo/koapp Server-Sent Events Middleware

Source: `src/middlewares/sse.js`.

KoaSSEMiddleware upgrades a Koa request to a Server-Sent Events (SSE) stream, setting the right headers and exposing ctx.sse.send(...) for pushing events to the browser over plain HTTP.

Use SSE when:

  • You need server → client streaming (notifications, logs, live charts)
  • You do NOT need client → server streaming (use koapp-apps WebSocket instead)
  • You want a simple, proxy-friendly transport - SSE works over HTTP/1.1

Browsers reconnect automatically; most reverse proxies handle it fine.

Import

javascript
const { KoaSSEMiddleware } = require('@axiosleo/koapp').middlewares;

Middleware factory

javascript
const middleware = KoaSSEMiddleware({
pingInterval: 60000, // ms between heartbeat comments (default 60s)
closeEvent: 'close' // event name sent on stream close
});

The factory returns a standard Koa middleware (ctx, next) => {}.

Wiring into a router handler

Because the framework already dispatches routes via its workflow, invoke the middleware inside the handler, passing context.koa:

javascript
const { Router } = require('@axiosleo/koapp');
const { KoaSSEMiddleware } = require('@axiosleo/koapp').middlewares;
const router = new Router('/');
router.any('/sse', async (context) => {
const sse = KoaSSEMiddleware();
await sse(context.koa, async () => {});
context.koa.sse.send({ data: 'hello, world!' });
process.nextTick(async () => {
for (let i = 0; i < 5; i++) {
context.koa.sse.send({ id: i, event: 'tick', data: { i } });
await new Promise((r) => setTimeout(r, 1000));
}
context.koa.sse.close();
});
});

Key points:

  1. Call await sse(context.koa, async () => {}) - pass a no-op next.
  2. After that, context.koa.sse exists.
  3. Push events with context.koa.sse.send(event); end with .close().
  4. Do not call success / failed / result in the same handler - SSE

takes over the response body.

Event shape

sse.send(data) accepts either a string or an object:

javascript
// String payload - becomes `data:<text>\n\n`
context.koa.sse.send('hello');
// Object payload - id/event are optional
context.koa.sse.send({
id: 42,
event: 'progress',
data: { percent: 75 } // objects are JSON-stringified
});

Raw wire output:

id:42
event:progress
data:{"percent":75}

Lifecycle methods

MethodWhen to use
ctx.sse.send(data)Push one event
ctx.sse.keepAlive()Send a :\n\n comment to keep the connection warm (called automatically every pingInterval ms for all active streams)
ctx.sse.close()Emit a final { event: closeEvent } and end the stream

The middleware maintains an internal ssePool and calls keepAlive on every registered stream every pingInterval ms. Manual calls are rarely needed.

Reacting to client disconnect

The middleware listens to close / error events on the SSE stream and calls an internal close() that:

  1. Removes the stream from the pool
  2. unpipe() + destroy() the stream
  3. Ends the Koa response
  4. Destroys the underlying socket

You can listen yourself too:

javascript
context.koa.sse.on('close', () => {
clearInterval(myPushLoop);
});

Long-lived handlers

The handler function must remain alive while the SSE stream is open. Don't return from it immediately; either keep pushing inline, or spawn a background task and let it own the lifecycle:

javascript
router.any('/logs', async (context) => {
const sse = KoaSSEMiddleware();
await sse(context.koa, async () => {});
// Detach so the handler resolves but the stream stays open
(async () => {
for await (const line of tail('./app.log')) {
context.koa.sse.send({ event: 'log', data: line });
}
context.koa.sse.close();
})();
});

The framework's workflow will see the handler resolve, but the response body (the SSE stream) remains open until you close() it or the client disconnects.

Browser client

javascript
const source = new EventSource('/sse');
source.onmessage = (e) => {
console.log('data:', e.data);
};
source.addEventListener('progress', (e) => {
const payload = JSON.parse(e.data);
console.log('progress:', payload.percent);
});
source.addEventListener('close', () => {
console.log('server closed the stream');
source.close();
});

Browsers automatically reconnect on network blips.

Pairing with other koapp pieces

  • Validate the upgrade with a router-level validator on query

(e.g. auth token) - koapp-router

  • Authenticate via session cookie - the middleware does not touch auth
  • Control backpressure by only pushing when context.koa.sse.writable

Common pitfalls

  • Calling success / failed / result / context.koa.body = ... in the

same handler conflicts with the SSE stream and yields incomplete output.

  • Using HTTP/2 requires enabling SSE on the server side - the default

Node HTTP/1.1 works out of the box.

  • Firewall / proxy buffering can delay events. Set

X-Accel-Buffering: no for nginx: ``javascript context.koa.set('X-Accel-Buffering', 'no'); ` Do this **before** calling sse(context.koa, ...)`.

  • The middleware's global setInterval heartbeat keeps all open SSE

connections warm; it does not leak across requests because each stream auto-removes itself from the pool on close.

See also

  • Real-time browser apps needing bi-directional traffic → koapp-apps (WebSocket)
  • Progress indicators for long-running HTTP jobs → this skill + Workflow from @axiosleo/cli-tool
  • Concrete SSE recipes: examples.md

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
44
Repository forks
11
7-day trend score
19

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/axiosleo/node-koapp/koapp-sse/badge)](https://verified-skill.com/skills/axiosleo/node-koapp/koapp-sse)

Something wrong with this skill or its page? Tell us.