---
url: https://node-ts.github.io/bus/guide/messages/system-messages.md
description: >-
  Handle messages from other systems, such as S3 notifications, that don't
  follow the bus' message conventions.
---

# System messages

System messages come from outside your application: an S3 notification that an object was created, a receipt from a support mailbox, or an alert that a server is low on disk space. This page covers subscribing to and handling them.

Because other systems create these messages, they don't follow the **@node-ts/bus** conventions, such as having a `$name`. They're received as they are, and a resolver function you provide decides which handler they go to.

::: tip
Have the handler of a system message publish an event of your own, as below. The rest of your application then handles your event, and doesn't depend on the other system's format.
:::

## Declaring a system message

Describe the message with an interface or class. If the other system doesn't publish a type for its messages, write one:

```ts
/**
 * The notification S3 publishes each time an object is created
 */
export interface S3ObjectCreatedNotification {
  Records: {
    eventSource: 'aws:s3'
    eventName: string
    s3: { object: { key: string } }
  }[]
}
```

## Handling a system message

Register the handler with `withCustomHandler()`. Its second argument has:

* `resolveWith`, which is called with every message read from the queue and returns whether this handler handles it. Messages could be anything, so check their shape.
* `topicIdentifier`, optionally, the topic the other system publishes to. The transport subscribes the service queue to it: for Amazon SQS it's an SNS topic ARN, and for RabbitMQ an exchange name. Without it, subscribe the queue yourself.

```ts
const bus = Bus.configure()
  .withMessageTypes(messageTypes)
  .withCustomHandler(
    async (notification: S3ObjectCreatedNotification, _attributes, ctx) => {
      // Publish your own event, so the rest of the app doesn't depend on S3's format
      for (const record of notification.Records) {
        await ctx.publish(new DocumentUploaded(record.s3.object.key))
      }
    },
    {
      // Called for every message read from the queue, which could be
      // anything, so check its shape

      resolveWith: notification =>
        Array.isArray(notification.Records) &&
        notification.Records[0]?.eventSource === 'aws:s3',
      // Subscribes the service queue to the topic S3 publishes to
      topicIdentifier: 'arn:aws:sns:us-east-1:000000000000:s3-object-created'
    }
  )
  .build()

await bus.initialize()
await bus.start()
```

Messages handled this way don't need [message types](/guide/serializers).

## See also

* [Events](/guide/messages/events)
* [Long running processes](/guide/long-running-processes), which can use system messages from a task scheduler
* [`CustomResolver`](/api/bus-core/interfaces/CustomResolver) in the API reference
