@talosjs/event component is a Redis-backed publish/subscribe layer. You declare an event as a class that extends RedisPubSub, give it a channel name, and implement a handler for incoming messages. Publishers call publish(data) and subscribers receive the same typed payload on the channel, so components communicate without knowing about each other, even across separate processes or server instances.
What it gives you
Publishers and subscribers share only a channel name and a data shape, never a direct reference, which keeps the two sides decoupled. Each event class is generic over itsData type, and that type carries through publish and into the handler. Everything for one event (getChannel(), handler(), publish(), and subscribe()) lives in a single RedisPubSub subclass. Because messages travel through Redis, every server instance subscribed to a channel receives them. You register the class with a decorator and resolve it, client already injected, from the container.
How it works
You extend the abstractRedisPubSub base class, declare a channel with getChannel(), and implement handler() for messages arriving on that channel. The base class wires publish, subscribe, unsubscribe, and unsubscribeAll to an injected RedisPubSubClient, which talks to Redis and JSON-serializes payloads on the way out and parses them on the way in.
getChannel() and handler() are the two abstract members you implement; the rest are provided by RedisPubSub. Data types must extend Record<string, ScalarType> so they round-trip cleanly through JSON. Malformed messages that fail to parse are silently ignored by the client.
Environment variables
The client reads
PUBSUB_REDIS_URL from the app environment when no connectionString is given in its options.
Decorator and usage
@decorator.event()
Registers an event class with the container. It accepts an optional scope (defaults to singleton). The decorated class gets its RedisPubSubClient injected, so you resolve the fully wired event from the container.
handler through Redis.
A channel can be dynamic. Compute it in getChannel() to scope subscriptions per tenant, room, or user:
Exceptions
The component throwsEventException when the client cannot connect or an operation fails. It carries a machine-readable key, a human-readable message, and a data object.
Working with channels
Tie each channel to a singleData type so publishers and subscribers stay in agreement. Subscribe once during bootstrap rather than per request, and call unsubscribe() on shutdown. Keep payloads serializable: data round-trips through JSON, so use scalar fields and steer clear of class instances, functions, and circular references.
Handle errors inside handler so a single bad message doesnβt stop processing; the handler should catch and route its own failures. Name channels with a stable scheme such as user-signed-up or user:123:events, which keeps even dynamic channels predictable. Let the container provide RedisPubSubClient instead of constructing connections by hand.
CLI command
Scaffold an event class and its test file with the generator. It writes the class undermodules/<module>/src/events/<Name>Event.ts, registers it in the module, and installs @talosjs/event if it is missing.
The generated class extends
RedisPubSub with the client injected, ready for you to type the payload and fill in the handler:
Use with Claude and Codex
The generator ships a matchingevent:create skill. It runs the scaffold and then guides your AI agent through completing the event: defining a real Data type, setting the channel in getChannel(), and implementing handler(). Initialize the skills once for your agent:
- Claude
- Codex
Prompt
event:create --name=UserSignedUp --channel=user-signed-up, then types the payload and implements handler().