HTTP Readable Streams
If you need to build custom endpoints that stream data progressively (like LLM text generation), Radiant provides a seamless ctx.stream() utility right on the request context.
This uses standard HTTP Readable Streams, meaning the connection stays open while the server writes iterative chunks directly to the response body.
Writing a Stream
app.router.get("/my-stream", async (ctx) => {
// Streams plain text sequentially
await ctx.stream("chunk 1", { delay: 500 });
await ctx.stream("chunk 2", { delay: 500 });
// Streams an object as JSON automatically
await ctx.stream({ finish: true });
});
Auto Server-Sent Events
You can mix custom HTTP Readable Streams with SSE by providing an event option. The framework will automatically format the chunks to strictly adhere to the event: and data: specification:
app.router.get("/ai-chat", async (ctx) => {
// Connection start
await ctx.stream({ status: "thinking" }, { event: "connection" });
// Iterative stream
for (const token of llm.generate()) {
await ctx.stream({ token }, { event: "message" });
}
// Close the stream automatically simply by returning from the handler!
}, { stream: true }); // Marks the route as a streaming endpoint for OpenAPI
Notice that passing { stream: true } in the route options will automatically tag this route's content-type as text/event-stream in your generated SDK and OpenAPI specs.
Advanced Usage
If you prefer lower-level control or are importing raw web streams, Radiant exports a helper stream() from the core package that accepts Iterables or AsyncGenerators:
import { stream } from "@codesordinatestudio/radiant-bun";
app.router.get("/raw-stream", (ctx) => {
function* generator() {
yield "Hello";
yield " ";
yield "World!";
}
return stream(generator());
});