Server-Sent Events
Lucent provides built-in Server-Sent Events support via a singleton sseManager and an Elysia plugin.
SSE is a good fit when you want one-way realtime updates from the server to the browser without the overhead of a full WebSocket connection.
Setup
Mount LucentSSE.plugin() in your main entry point:
// src/index.ts
import { Elysia } from "elysia";
import { lucent, getLucent, LucentSSE, resolveLucentUser } from "@codesordinatestudio/lucent";
import config from "./lucent.config";
const lucentApi = await getLucent({ config });
const app = new Elysia().use(await lucent(config));
app.use(
LucentSSE.plugin({
path: "/sse",
resolveUser: (request) => resolveLucentUser(request, lucentApi),
}),
);
app.listen(3000);
If you already have a Lucent Local API instance, the same helper works anywhere:
import { getLucent, LucentSSE, resolveLucentUser } from "@codesordinatestudio/lucent";
import config from "./lucent.config";
const lucent = await getLucent({ config });
app.use(
LucentSSE.plugin({
path: "/sse",
resolveUser: (request) => resolveLucentUser(request, lucent),
}),
);
Usage in Hooks
Broadcast events from anywhere via the sseManager singleton:
// src/collections/Posts.ts
import { sseManager, defineCollection } from "@codesordinatestudio/lucent";
export const Posts = defineCollection({
slug: "posts",
fields: [
/* ... */
],
hooks: {
afterCreate: [
async ({ doc }) => {
sseManager.broadcastToChannel("posts", {
event: "post:created",
data: doc,
});
},
],
afterUpdate: [
async ({ doc }) => {
sseManager.broadcastToChannel("posts", {
event: "post:updated",
data: doc,
});
},
],
},
});
Manager API
sseManager is a singleton available throughout your app. You can also access it via LucentSSE.manager.
Channels
| Method | Description |
|---|---|
subscribe(id, channel) | Add a connection to a channel |
unsubscribe(id, channel) | Remove a connection from a channel |
getChannelMembers(channel) | Returns connection IDs subscribed to channel |
getConnectionChannels(id) | Returns all subscribed channels for a client |
listChannels() | Lists all active channels |
Sending Messages
| Method | Description |
|---|---|
sendTo(id, payload) | Send a message to a single subscriber |
broadcastToChannel(channel, payload) | Send to everyone on a channel |
broadcastAll(payload, options?) | Send to every connected SSE client |
Metadata
| Method | Description |
|---|---|
setMeta(id, key, value) | Store arbitrary metadata on a connection |
getMeta(id, key) | Read metadata from a connection |
Stats
sseManager.getStats();
// { connections: 12, channels: { posts: 8, notifications: 12 } }
Client-Side Usage
const stream = new EventSource("/sse?channels=posts,notifications", {
withCredentials: true,
});
stream.addEventListener("connected", (event) => {
console.log("connected", JSON.parse(event.data));
});
stream.addEventListener("post:created", (event) => {
const post = JSON.parse(event.data);
console.log("new post", post);
});
stream.addEventListener("ping", () => {
// heartbeat event
});
Authentication
resolveLucentUser() lets SSE reuse Lucent's existing auth stack:
Authorization: Bearer <token>- Lucent JWT cookies
- Lucent session cookies
x-api-key
That makes it the easiest way to authenticate both SSE and custom HTTP handlers without rewriting token parsing.
import { LucentSSE, getLucent, resolveLucentUser } from "@codesordinatestudio/lucent";
import config from "./lucent.config";
const lucent = await getLucent({ config });
app.use(
LucentSSE.plugin({
path: "/sse",
resolveUser: (request) => resolveLucentUser(request, lucent),
onConnect: ({ user }) => !!user,
onSubscribe: ({ user, channel }) => {
if (!user) return false;
if (channel.startsWith("private-") && user.role !== "admin") return false;
return true;
},
}),
);
Authorization Hooks
Use onConnect to allow or reject the initial stream, and onSubscribe to control channel access.
LucentSSE.plugin({
path: "/sse",
resolveUser: (request) => resolveLucentUser(request, lucent),
onConnect: ({ user }) => {
if (!user) return "Authentication required";
return true;
},
onSubscribe: ({ user, channel }) => {
if (!user) return false;
if (channel === "admin" && user.role !== "admin") return false;
return true;
},
});
Return values:
trueto allowfalseto deny- a
stringto deny with a custom error message
Heartbeat
LucentSSE sends a heartbeat event by default every 30 seconds to keep the stream active through proxies and load balancers.
LucentSSE.plugin({
path: "/sse",
heartbeat: { interval: 60_000, event: "keepalive", data: { ok: true } },
});
LucentSSE.plugin({
path: "/sse",
heartbeat: false,
});
Route Behavior
The SSE endpoint accepts channels in either form:
/sse?channel=posts&channel=notifications
/sse?channels=posts,notifications
On connect, Lucent sends a connected event with the generated connection id, subscribed channels, and whether the request was authenticated.