Server-side subscriptions
Read Realtime messages in server code, forward them over Server-Sent Events, or react to them in a worker.
Subscribe from your server when a browser can't hold a WebSocket, when you want to forward updates through an existing HTTP response, or when another service needs to react to a function's progress. Server code that has your Inngest client authenticates with its signing key, so it doesn't need a subscription token.
Read a stream
inngest.realtime.subscribe() returns a readable stream of typed messages. Iterate it with for await.
import { inngest } from "./inngest/client";
import { jobChannel } from "./inngest/channels";
const stream = await inngest.realtime.subscribe({
channel: jobChannel({ jobId: "job_123" }),
topics: ["status", "result"],
});
for await (const message of stream) {
if (message.kind === "run") continue;
console.log(message.topic, message.data);
}
subscribe({ app: inngest, channel, topics }) from inngest/realtime does the same when you pass the client as an argument.
Handle messages with a callback
Pass onMessage to receive each message in a callback instead of a stream. The call returns a subscription handle. Pass onError to catch connection errors.
const subscription = await inngest.realtime.subscribe({
channel: jobChannel({ jobId: "job_123" }),
topics: ["status"],
onMessage: (message) => {
console.log(message.data);
},
onError: (err) => {
console.error("Realtime subscription failed", err);
},
});
// Later, when you no longer need updates:
subscription.close();
Close every subscription when its consumer finishes so the connection doesn't stay open.
Forward updates with Server-Sent Events
getEncodedStream() returns bytes you can return as a text/event-stream response. This route starts a function and streams its updates back in the same response.
app/api/hello-world/route.tsimport { inngest } from "@/inngest/client";
import { helloChannel } from "@/inngest/channels";
export async function POST() {
const uuid = crypto.randomUUID();
const channel = helloChannel({ uuid });
// Subscribe first so the stream includes the function's first update.
const stream = await inngest.realtime.subscribe({
channel,
topics: ["logs"],
});
await inngest.send({ name: "hello-world/hello", data: { uuid } });
return new Response(stream.getEncodedStream(), {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
},
});
}
Authorize the caller in this route, just as you would before you mint a token. The route reads with your signing key, so it can read any channel.
The stream also offers getJsonStream() for a stream of parsed messages and close() to end the subscription.
Next steps
- Subscribing reference lists every option and message field.
- Subscription tokens covers browser subscriptions.
- Troubleshooting helps when a server subscriber receives nothing.