
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@webqit/node-live-response
Advanced tools
This package brings LiveResponse to traditional Node.js and Express servers.
LiveResponse extends the HTTP request/response model with persistent interactivity. You send a response — and keep it open as a live communication channel.
Instead of closing after delivery, the response becomes interactive.
For the full conceptual model, see the LiveResponse docs.
If you’re building a system where live interactivity is a first-class architectural primitive, use Webflo. Live responses are native there. This package exists for cases where you want LiveResponse inside an otherwise conventional Node.js or Express backend.
npm install @webqit/node-live-response
The first thing you do is enable live mode on your HTTP server instance. This installs the transport layer and request bookkeeping required for live sessions.
import http from 'http';
import { enableLive } from '@webqit/node-live-response';
const server = http.createServer(handler);
const liveMode = enableLive(server);
server.listen(3000);
import express from 'express';
import { enableLive } from '@webqit/node-live-response';
const app = express();
const server = app.listen(3000);
const liveMode = enableLive(server);
The returned liveMode function enables live mode per route.
It works both as:
async function handler(req, res) {
liveMode(req, res);
const liveRes = new LiveResponse('Hello world');
await res.send(liveRes); // resolves when the live connection is established
// ---- interactive phase ----
setTimeout(() => {
res.die(); // explicitly end live mode
}, 5000);
}
app.get('/counter', liveMode(), async (req, res) => {
const liveRes = new LiveResponse('Hello world');
await res.send(liveRes); // resolves when the live connection is established
// ---- interactive phase ----
setTimeout(() => {
res.die();
}, 5000);
});
LiveResponse supports three core interaction patterns.
Send a mutable object as the response body.
Mutations on the server automatically propagate to the client.
(More in the LiveResponse docs)
On the server:
import { Observer } from '@webqit/observer';
app.get('/counter', liveMode(), async (req, res) => {
const state = { count: 0 };
const liveRes = new LiveResponse(state);
await res.send(liveRes);
const interval = setInterval(() => {
Observer.set(state, 'count', state.count + 1);
}, 1_000);
setTimeout(() => {
clearInterval(interval);
res.die();
}, 60_000);
});
On the client:
<!doctype html>
<head>
<title>Live Counter</title>
<script src="https://unpkg.com/@webqit/fetch-plus/dist/main.js"></script>
</head>
<body>
<h1></h1>
<script type="module">
const { LiveResponse, Observer } = window.webqit;
const { body: state } = await LiveResponse.from(fetch('/counter')).now();
Observer.observe(state, () => {
document.querySelector('h1').textContent = 'Count: ' + state.count;
});
</script>
</body>
</html>
[!TIP] This example can be previewed live by running:
cd node-live-response node test/server1.jsOpen localhost:3000 to view
Replace the current response with a new one — without issuing a new HTTP request.
This enables a multi-response architecture over a single request.
(More in the LiveResponse docs)
On the server:
app.get('/news', liveMode(), async (req, res) => {
const liveRes = new LiveResponse(
{ headline: 'Breaking: Hello World' },
{ done: false }
);
await res.send(liveRes);
setTimeout(() => {
liveRes.replaceWith(
{ headline: 'Update: Still Hello World' },
{ done: false }
);
}, 3_000);
setTimeout(() => {
liveRes.replaceWith({ headline: 'Final: Goodbye' });
}, 6_000);
setTimeout(() => {
res.die();
}, 60_000);
});
On the client:
<!doctype html>
<head>
<title>Live News</title>
<script src="https://unpkg.com/@webqit/fetch-plus/dist/main.js"></script>
</head>
<body>
<h1></h1>
<script type="module">
const { LiveResponse } = window.webqit;
const liveRes = LiveResponse.from(fetch('/news'));
liveRes.addEventListener('replace', (e) => {
document.querySelector('h1').textContent = e.data.body.headline;
});
</script>
</body>
[!TIP] This example can be previewed live by running:
cd node-live-response node test/server2.jsOpen localhost:3000 to view
Exchange messages between client and server through a message port.
(More in the LiveResponse docs)
On the server:
app.get('/chat', liveMode(), async (req, res) => {
const liveRes = new LiveResponse({ title: 'Chat' });
await res.send(liveRes);
req.port.addEventListener('message', (e) => {
req.port.postMessage(e.data);
});
setTimeout(() => {
res.die();
}, 60_000);
});
On the client:
<!doctype html>
<head>
<title>Live Chat</title>
<script src="https://unpkg.com/@webqit/fetch-plus/dist/main.js"></script>
</head>
<body>
<h1>Chat</h1>
<ul id="log"></ul>
<input id="msg" placeholder="Type and press enter" />
<script type="module">
const { LiveResponse } = window.webqit;
const { port } = await LiveResponse.from(fetch('/chat')).now();
port.addEventListener('message', (e) => {
const li = document.createElement('li');
li.textContent = e.data;
log.appendChild(li);
});
const msg = document.querySelector('#msg');
msg.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
port.postMessage(msg.value);
msg.value = '';
}
});
</script>
</body>
[!TIP] This example can be previewed live by running:
cd node-live-response node test/server3.jsOpen localhost:3000 to view
@webqit/node-live-response augments the standard Node/Express request lifecycle:
req.port for bidirectional messagingreq.signal for live session lifecycle trackingres.send() / res.end() to accept LiveResponseres.die() to explicitly terminate live interactionLive interaction has its own lifecycle, separate from the HTTP lifecycle.
Interactivity begins when you send a LiveResponse:
await res.send(liveRes);
That is the moment the client learns that the response is interactive and joins the live channel.
The send() method, this time, returns promise that resolves when the client has joined the live channel.
You may await this promise where necessary, but messages or response swaps issued before the connection is fully established are automatically queued and flushed once live mode becomes active.
The transition to an "open" connection may also be observed via:
await req.port.readyStateChange('open');
Live interaction ends when the LiveResponse port on the client side is closed or when you explicitly call:
res.die();
on the server. This method aborts the request lifecycle signal exposed at req.signal.
Note that ending the HTTP response does not end the live session.
The HTTP lifecycle and the live lifecycle are independent.
The transition to a "closed" connection may be observed via:
await req.port.readyStateChange('close');
MIT
FAQs
LiveResponse for Node.js & Express
We found that @webqit/node-live-response demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.