# Server events

https://docs.serverpod.dev/next/concepts/endpoints-and-apis/server-events

Serverpod has a built-in event system: post a message to a named channel, and every listener on that channel receives it, even on other servers in a cluster. It is how one user's action reaches everyone else, like the chat-room updates in [streaming's live example](https://docs.serverpod.dev/next/concepts/endpoints-and-apis/streaming.md#example-live-updates-for-a-filtered-query). You access it through `session.messages`.

## Send messages

Send a message with the `postMessage` method. The message is published to the specified channel and must be a [data model](https://docs.serverpod.dev/next/concepts/data-and-the-database/models.md).

```dart
var message = UserUpdate(); // Model that represents changes to user data.
await session.messages.postMessage('user_updates', message);
```

In the example above, the message is published on the `user_updates` channel. Any subscriber to this channel in the server will receive the message.

### Message scope

Serverpod uses Redis to pass messages between servers. A message is sent to every server connected to the same Redis instance when [Redis is enabled](https://docs.serverpod.dev/next/concepts/server-fundamentals/configuration.md), and stays on the current server when it is not. Set the `scope` parameter to choose the delivery explicitly.

| Scope                 | Delivery                                                                |
| --------------------- | ----------------------------------------------------------------------- |
| `MessageScope.auto`   | Cluster-wide if Redis is enabled, otherwise local. This is the default. |
| `MessageScope.global` | Cluster-wide. Throws a `StateError` if Redis is not enabled.            |
| `MessageScope.local`  | Only to subscribers on the current server.                              |

The default already goes cluster-wide when Redis is enabled. Pass `MessageScope.local` to keep a message on the current server:

```dart
var message = UserUpdate(); // Model that represents changes to user data.
await session.messages.postMessage(
  'user_updates',
  message,
  scope: MessageScope.local,
);
```

## Receive messages

Serverpod provides two ways to handle incoming messages: by creating a stream that subscribes to a channel or by adding a listener to a channel.

### Create a stream

To create a stream that subscribes to a channel, use the `createStream` method. The stream emits a value whenever a message is posted to the channel. The typed variant, `createStream<UserUpdate>(...)`, emits an error if a posted message does not match the type.

```dart
var stream = session.messages.createStream('user_updates');
stream.listen((message) {
  print('Received message: $message');
});
```

In the above example, a stream is created that listens to the `user_updates` channel and processes incoming messages.

#### Stream lifecycle

The stream is automatically closed when the session is closed. To close the stream manually, cancel the stream subscription.

```dart
var stream = session.messages.createStream('user_updates');
var subscription = stream.listen((message) {
    print('Received message: $message');
});

subscription.cancel();
```

### Add a listener

To add a listener to a channel, use the `addListener` method. The listener will be called whenever a message is posted to the channel.

```dart
session.messages.addListener('user_updates', (message) {
  print('Received message: $message');
});
```

In the above example, the listener will be called whenever a message is posted to the `user_updates` channel. Listeners receive every message that reaches this server, whatever its scope.

#### Listener lifecycle

The listener is automatically removed when the session is closed. To manually remove a listener, use the `removeListener` method.

```dart
var myListenerCallback = (message) {
  print('Received message: $message');
};
// Register the listener
session.messages.addListener('user_updates', myListenerCallback);

// Remove the listener
session.messages.removeListener('user_updates', myListenerCallback);
```

In the above example, the listener is first added and then removed from the `user_updates` channel.

## Broadcast revoked authentication

The messaging interface also carries authentication revocation events: `session.messages.authenticationRevoked(userIdentifier, message)` notifies every server that a user's authentication changed, falling back to local delivery when Redis is disabled. For when and how to call it, see [handling revoked authentication](https://docs.serverpod.dev/next/concepts/authentication/custom-overrides.md#handling-revoked-authentication).
