---
question: Should I publish for fanout or enqueue for a worker?
kind: task
via_web: na
via_cli: na
via_python: available
via_api: na
via_config: na
ref_python: bus.publish
prereq: An in-engine module that lists `BusService` in its dependencies. [Create a module](/user-guide/build/modules/creating-a-module) first.
next:
  - /capabilities/build/platform/consume-a-queue
  - /capabilities/build/platform/message-bus-overview
---

# Publish a message

The bus has two ways to send. `publish` is fanout: every subscriber that is listening gets a copy, and nothing is kept for one that is not. `enqueue` puts the message on a work queue: it waits until exactly one worker takes it.

## Web UI

Not applicable. Nexus does not send on the bus. In NATS mode, super admins can see what reached a JetStream stream under **System** › **Data** › **Bus**. Use the Python steps on this page.

## CLI

Not applicable. There is no `abi` command that publishes on the bus. Use the Python steps on this page. The command list is the [CLI reference](/user-guide/reference/cli).

## Python

### Declare the service

```python title="src/my_module/__init__.py"
from naas_abi_core.module.Module import BaseModule, ModuleDependencies
from naas_abi_core.services.bus.BusService import BusService


class ABIModule(BaseModule):
    dependencies: ModuleDependencies = ModuleDependencies(
        modules=[],
        services=[BusService],
    )
```

### Publish to subscribers

```python
import json

bus = self.engine.services.bus
bus.publish("my_module.orders", "created", json.dumps({"id": 1}).encode())
```

The arguments are the topic, the routing key and the payload as `bytes`. Subscribers on `my_module.orders` with the routing key `created`, `*` or `#` get it. Topics are shared by every module, so prefix yours.

### Or hand it to one worker

Use a topic of its own for the work queue:

```python
bus.enqueue("my_module.order_jobs", "created", json.dumps({"id": 1}).encode())
```

The message goes to one worker on that topic and routing key, and waits until one takes it. It survives a restart on NATS, RabbitMQ and `python_queue` with a `persistence_path` (the engine default, `storage/bus/python_queue.sqlite3`). A `python_queue` block written with `config: {}` has no file and keeps the queue in memory. The worker is [Consume a queue](/user-guide/build/platform/consume-a-queue).

:::warning One model per topic
Do not `publish` and `enqueue` on the same topic. In NATS mode the work queue of a topic is a JetStream stream that captures every subject under it, so once a queue exists, plain publishes on that topic are stored too and handed to its workers.
:::

### Send a batch

```python
bus.publish_many("my_module.orders", [("created", b"..."), ("created", b"...")])
```

Subscribers see the same messages as from one `publish` per pair; the adapter may send them in one round trip.

## REST API

Not applicable. No Nexus API route sends on the bus. The System routes read JetStream streams in NATS mode, and a write answers 405. Use the Python steps on this page.

## Config

Not applicable. A message is a call. In NATS mode the bus is JetStream on the broker; without NATS, `services.bus` picks `python_queue` or `rabbitmq`. See the [Configuration Reference](/user-guide/get-started/configuration#message-bus).

## Good to know

- **Errors.** A send that fails raises, and records a `BusError` event.
- **Topics starting with `evt.`** belong to the event service. Publish events with [Record an event](/user-guide/build/platform/record-an-event), not on the bus.
