Streaming and Web Payloads
Stream Web ReadableStreams, binary buffers, Blobs, and Server-Sent Events with edge-compatible helpers from @taserjs/router/stream.
@taserjs/router/stream exposes Web Standard helpers — pipe, buffer, blob, and sse — that work the same on Node, Bun, Deno, Workers, and other fetch runtimes. There is no filesystem file() helper; serve disk content through your host platform or by wrapping a ReadableStream / Blob yourself.
Piping Readable Streams (pipe)
Stream dynamically generated data, AI completions, or proxied upstream bodies:
import { pipe } from "@taserjs/router/stream";
import { t } from "@taserjs/router";
export default t.post("/ai/generate").handler(async () => {
const encoder = new TextEncoder();
const customStream = new ReadableStream({
async start(controller) {
for (const word of ["Streaming", " live", " AI", " response", "..."]) {
controller.enqueue(encoder.encode(word));
await new Promise((resolve) => setTimeout(resolve, 200));
}
controller.close();
},
});
return pipe(customStream, {
headers: {
"Content-Type": "text/plain; charset=utf-8",
},
});
});Binary Payloads (buffer)
Return generated images, PDFs, or other byte arrays:
import { buffer } from "@taserjs/router/stream";
import { t } from "@taserjs/router";
export default t.get("/export/pdf").handler(async ({ req }) => {
const pdfBytes: Uint8Array = await generateInvoicePdf(req.query);
return buffer(pdfBytes, {
headers: {
"Content-Type": "application/pdf",
"Content-Disposition": 'attachment; filename="invoice.pdf"',
},
});
});buffer() defaults Content-Type to application/octet-stream when you omit it.
Blob Payloads (blob)
Pass a Blob (for example from fetch or in-memory construction) and preserve its type when present:
import { blob } from "@taserjs/router/stream";
import { t } from "@taserjs/router";
export default t.get("/assets/logo").handler(async () => {
const upstream = await fetch("https://cdn.example.com/logo.png");
const logo = await upstream.blob();
return blob(logo, {
headers: {
"Cache-Control": "public, max-age=86400",
},
});
});Server-Sent Events (sse)
Use sse() for the W3C Event Stream protocol. It sets Content-Type: text/event-stream and Cache-Control: no-cache for you:
import { sse } from "@taserjs/router/stream";
import { t } from "@taserjs/router";
export default t.get("/events/notifications").handler(({ req }) => {
return sse(
async (stream) => {
await stream.write({ event: "connected", data: { ok: true } });
const interval = setInterval(() => {
void stream.write({
event: "tick",
data: { time: new Date().toISOString() },
});
}, 1000);
stream.onAbort(() => clearInterval(interval));
},
{ signal: req.raw.signal },
);
});formatSSE
Prefer sse() for responses. Import formatSSE when you need to encode individual messages into
SSE text for a custom ReadableStream passed to pipe().
Next Steps
Response Contracts
Enforce compile-time return shape safety and runtime response validation with .returns(). Eliminate response drift between backend and frontend.
Global Error Handling
Catch unhandled runtime crashes with .onError() and customize 404s with .notFound(). Protocol errors (422/415) and response contracts bypass onError.