---
term: 'Pub/Sub (Publish-Subscribe) Pattern'
seoTitle: 'Pub/Sub Pattern Explained: Topics, Brokers, Delivery Guarantees'
headline: 'What is the Pub/Sub (Publish-Subscribe) Pattern?'
slug: pub-sub-pattern
category: api-realtime
shortDefinition: 'Pub/sub is a messaging pattern where publishers send messages to topics on a broker, and every subscriber to a topic gets a copy.'
relatedTerms:
  - event-driven-architecture
  - websockets-real-time-sync
  - real-time-live-queries
  - sse-vs-websockets-vs-polling
contrastsWith:
  - webhooks
aboutTerms:
  - 'Publisher'
  - 'Subscriber'
  - 'Topic'
  - 'Message Broker'
faq:
  - question: 'What is pub/sub in simple terms?'
    answer: 'A messaging pattern where senders (publishers) post messages to named topics on a broker, and every component that subscribed to that topic receives its own copy — publishers and subscribers never know about each other. Think radio broadcast: the station transmits on a frequency; whoever tunes in, hears it.'
  - question: 'What is the difference between pub/sub and a message queue?'
    answer: 'A queue distributes work; pub/sub duplicates it. In a queue, each message is consumed by exactly one worker — the tool for spreading jobs across a pool. In pub/sub, every subscriber to the topic gets a copy — the tool for broadcasting events to independently interested parties.'
  - question: 'What is the difference between pub/sub and the observer pattern?'
    answer: 'Observer is in-process: the subject holds direct references to its observers and usually calls them synchronously. Pub/sub inserts a broker between the parties, making them fully decoupled, typically asynchronous, and able to live in different processes, languages, and deployments. One is a design pattern inside an app; the other is an architecture between systems.'
  - question: 'How is pub/sub different from request-response?'
    answer: 'Request-response is synchronous and one-to-one — the caller blocks until an answer returns. Pub/sub is asynchronous, one-way, and one-to-many: publish returns immediately and no reply exists. If a subscriber must answer, that is a separate message on a reply channel, not a return value.'
  - question: 'What is a topic in pub/sub?'
    answer: 'A named logical channel that categorizes messages: publishers address the topic, and the broker delivers each message to all of that topic''s subscribers. Filtering can be topic-based (subscribe by name, often hierarchically — orders.*) or content-based, where the broker matches on message attributes.'
  - question: 'What delivery guarantees does pub/sub offer?'
    answer: 'Three levels. At-most-once: fire and forget — fast, may lose messages. At-least-once: retry until acknowledged — the common default, produces occasional duplicates. Exactly-once: deduplication plus coordination — expensive, constrained, and impossible to guarantee end-to-end in general. The practical stance: at-least-once delivery with idempotent handlers.'
  - question: 'Does pub/sub guarantee message ordering?'
    answer: 'Not by default — messages fan out to parallel subscribers, and global order fights parallelism. Brokers can order within a partition or ordering key at a throughput cost. The robust design assumes ordering only where explicitly configured and writes handlers that tolerate out-of-order arrival.'
  - question: 'Is Kafka pub/sub?'
    answer: 'Yes, with a twist: Apache Kafka implements publish-subscribe semantics over a partitioned, durable, replayable commit log. Within a consumer group it behaves like a queue (each message to one member); across groups it behaves like pub/sub (each group gets a copy) — which is why it is usually called event streaming.'
  - question: 'When should you not use pub/sub?'
    answer: 'When the caller needs an immediate answer (request-response), when operations must be atomic across producer and consumers (a broker cannot join a transaction — see the Saga pattern), when strict global ordering is required, or when the system is small enough that a broker adds more operational surface than it removes.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'Publish-Subscribe Channel — Enterprise Integration Patterns'
    url: 'https://www.enterpriseintegrationpatterns.com/patterns/messaging/PublishSubscribeChannel.html'
  - name: 'MQTT 5.0 specification — OASIS'
    url: 'https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html'
  - name: 'Apache Kafka documentation — design'
    url: 'https://kafka.apache.org/documentation/#design'
  - name: 'What do you mean by "Event-Driven"? — Martin Fowler'
    url: 'https://martinfowler.com/articles/201701-event-driven.html'
cta:
  title: 'Publish by saving, subscribe by querying'
  text: 'Back4app Live Queries are pub/sub at the edge: any client subscribes to a query as its topic, any write publishes to every matching subscriber — broker, fan-out, and permissions run by the platform.'
  linkText: 'Start building free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-24'
translationKey: pub-sub-pattern
---

**Pub/sub is a messaging pattern where publishers send messages to topics on a broker, and every subscriber to a topic gets a copy.** Its whole value is what the parties *don't* know: publishers never learn who is listening, subscribers never learn who sent — the broker's indirection decouples them in space (different systems), time (subscribers may be offline), and synchronization (publish returns immediately). Add subscribers without touching publishers; that is the pattern's quiet superpower.

## Key takeaways

| Question | Answer |
| --- | --- |
| The cast | Publisher → message → topic → broker → subscribers (each gets a copy) |
| vs. a queue | A queue *distributes* work to one consumer; pub/sub *duplicates* it to all |
| vs. observer | Observer is in-process and synchronous; pub/sub is brokered and async |
| The default stance | At-least-once delivery + idempotent handlers |
| The fine print | No replies, no default ordering, no atomic transactions across the broker |

## The pattern in code

Every pub/sub API reduces to two verbs, whatever the broker:

```js
// Subscribers — register interest in a topic
broker.subscribe('orders.paid', (msg) => fulfill(msg));   // service A
broker.subscribe('orders.paid', (msg) => notifyUser(msg)); // service B — its own copy

// Publisher — fire and forget
broker.publish('orders.paid', { orderId: 'o-1187' });
// The publisher never learns who received it — or whether anyone did.
```

The same shape at the client edge, where a live-query subscription is the topic and a database write is the publish:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// Pub/sub at the edge: live queries subscribe, saves publish
// Subscriber — register interest in a topic
const topic = new Parse.Query('Event');
topic.equalTo('topic', 'orders');
const sub = await topic.subscribe();
sub.on('create', (event) => handle(event.get('payload')));

// Publisher — fire and forget; no knowledge of subscribers
const event = new Parse.Object('Event');
event.set('topic', 'orders');
event.set('payload', { orderId: 'o-1187', status: 'paid' });
await event.save();
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// Pub/sub at the edge: live queries subscribe, saves publish
// Subscriber — register interest in a topic
final liveQuery = LiveQuery();
final topic = QueryBuilder<ParseObject>(ParseObject('Event'))
  ..whereEqualTo('topic', 'orders');
final sub = await liveQuery.client.subscribe(topic);
sub.on(LiveQueryEvent.create, (event) => handle(event.get('payload')));

// Publisher — fire and forget; no knowledge of subscribers
final event = ParseObject('Event')
  ..set('topic', 'orders')
  ..set('payload', {'orderId': 'o-1187', 'status': 'paid'});
await event.save();
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// Pub/sub at the edge: live queries subscribe, saves publish
// Subscriber — register interest in a topic
let topic = Event.query("topic" == "orders")
let sub = try await topic.subscribe()
sub.handleEvent { _, e in
    if case .created(let event) = e { handle(event.payload) }
}

// Publisher — fire and forget; no knowledge of subscribers
var event = Event()
event.topic = "orders"
event.payload = ["orderId": "o-1187", "status": "paid"]
try await event.save()
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// Pub/sub at the edge: live queries subscribe, saves publish
// Subscriber — register interest in a topic
val client = ParseLiveQueryClient.Factory.getClient()
val topic = ParseQuery.getQuery<ParseObject>("Event")
topic.whereEqualTo("topic", "orders")
val sub = client.subscribe(topic)
sub.handleEvent(SubscriptionHandling.Event.CREATE) { _, event ->
    handle(event.getJSONObject("payload"))
}

// Publisher — fire and forget; no knowledge of subscribers
val event = ParseObject("Event")
event.put("topic", "orders")
event.put("payload", JSONObject(mapOf("orderId" to "o-1187", "status" to "paid")))
event.save()
```

## How pub/sub works

```mermaid
flowchart LR
  accTitle: Publish-subscribe message flow through a broker
  accDescr: A publisher sends a message to a named topic on a message broker. The broker matches the topic against registered subscriptions and delivers an independent copy of the message to each subscriber, which acknowledge processing separately.
  P["Publisher<br/>(fire & forget)"] -->|"publish(orders.paid, msg)"| T["Broker<br/>topic: orders.paid"]
  T -->|"copy 1"| S1["Subscriber A<br/>fulfillment"]
  T -->|"copy 2"| S2["Subscriber B<br/>notifications"]
  T -->|"copy 3"| S3["Subscriber C<br/>analytics"]
```

The broker does four jobs: **accept** publishes and return immediately; **match** each message against subscriptions (by topic name, hierarchically — `orders.*` — or by content attributes); **deliver** an independent copy per subscriber, retrying per its guarantee level; and **retain** messages for offline subscribers where durability is configured — the time-decoupling that lets a service deploy at noon and catch up on what it missed. One vocabulary note worth its sentence: a *message* is the envelope; an *event* — "this happened, past tense" — is what pub/sub usually carries, and whether it carries just the fact or the full state is [Fowler's](https://martinfowler.com/articles/201701-event-driven.html) event-notification vs. event-carried-state-transfer distinction.

## Pub/sub vs. message queues vs. observer pattern

The two confusions, settled in one table:

| | Pub/sub | Message queue | Observer |
| --- | --- | --- | --- |
| Delivery | Every subscriber gets a copy | Exactly one worker consumes each message | Subject calls each observer |
| Purpose | Broadcast events | Distribute work | React to state in-process |
| Coupling | None — broker-mediated | None — queue-mediated | Direct object references |
| Timing | Async | Async | Usually synchronous |
| Boundary | Across processes/systems | Across workers | Inside one process |
| Canonical shape | 1 event → N reactions | N jobs → M workers | 1 object → its listeners |

The soundbite that survives the meeting: **a queue distributes work; pub/sub duplicates it.** And observer is not "small pub/sub" — the broker's indirection is precisely what observer lacks, which is why refactoring an observer into pub/sub is an architectural change, not a rename. In practice brokers offer both shapes: consumer groups turn a topic into a queue per group ([Kafka's](https://kafka.apache.org/documentation/#design) signature move), fanout exchanges turn a queue system into pub/sub.

## Delivery guarantees: at-most-once vs. at-least-once vs. exactly-once

| Guarantee | Failure mode | Cost | Your handler must | Choose when |
| --- | --- | --- | --- | --- |
| At-most-once | Messages can vanish | Cheapest, fastest | Tolerate gaps | Ephemeral data: presence, tickers, metrics |
| At-least-once | Duplicates arrive | Acks + retries | **Be idempotent** | The default for business events |
| Exactly-once | Constrained, complex | Dedup + coordination | Still be idempotent | Narrow paths where brokers support it |

[MQTT's](https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.html) QoS levels 0/1/2 map to these three exactly — a tidy confirmation the trichotomy is fundamental, not vendor marketing. The engineering truth: end-to-end exactly-once is unachievable in general (the Two Generals' problem wearing a broker badge); systems that advertise it combine at-least-once delivery with deduplication. Which is why the pattern's real contract is written in your handler: **at-least-once + idempotency** — dedup on a message ID, use upserts keyed on the entity, or version-check before applying — turns duplicates from bugs into no-ops.

## Ordering, dead letters, and topic design

Three operational realities the intro diagrams skip. **Ordering:** fan-out to parallel consumers has no global order; brokers restore it per partition or ordering key at the price of parallelism — design handlers to apply events by entity ID and version, not arrival sequence, and the problem mostly dissolves. **Poison messages:** a message whose processing always throws will retry forever under at-least-once; cap delivery attempts and route failures to a **dead-letter queue** for triage, or one bad event stalls the topic. **Topic design:** name hierarchically (`orders.paid`, `orders.refunded`, wildcard `orders.*`), keep granularity at the level subscribers actually differ on, and evolve message schemas additively — subscribers ignore unknown fields; breaking changes get a new versioned topic (`orders.v2`) with a migration window, exactly like an API version.

## Common use cases

- **Microservice integration** — services owned by different teams reacting to each other's [events](/glossary/decoupled-architecture/) without direct calls or shared deploy schedules.
- **Real-time client features** — chat fan-out, live dashboards, [presence](/glossary/presence-online-status/): pub/sub over WebSockets at the edge.
- **IoT telemetry** — thousands of devices publishing over MQTT; consumers from alerting to analytics subscribing independently.
- **Cache invalidation and replication** — one write published, every cache and replica notified.
- **Parallel processing pipelines** — one event triggering fulfillment, notification, and analytics simultaneously, each at its own pace.

## Should you use pub/sub? A decision matrix

| Pub/sub fits when… | Prefer something else when… |
| --- | --- |
| One event interests several independent consumers | One consumer: a queue, or just a call |
| Producers and consumers deploy independently | Caller needs the answer now → request-response |
| Eventual consistency is acceptable | Atomicity across parties required → transactions/Saga |
| Subscribers come and go over time | Strict global ordering is non-negotiable |
| Spiky load needs buffering between systems | The whole system fits in one process → observer |

## Limitations and trade-offs

- **Debugging crosses a broker.** "Who consumed this and why did it fail?" needs correlation IDs and tracing; the decoupling that frees teams also hides causality.
- **No replies by design.** Workflows needing responses must model them as further messages — added latency and state machines where a function call once sufficed.
- **The broker is infrastructure.** Availability, capacity, security, and retention policies of the messaging layer become platform work; a down broker is an outage multiplier.
- **Publisher blindness cuts both ways.** Publishers can't observe subscriber health; a silently failing consumer loses data unless dead-letter monitoring exists.
- **Guarantees cost throughput.** Ordering keys serialize, exactly-once coordinates, durability persists — each strengthening of semantics spends performance; buy only what the data needs.

## Pub/sub on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. The pattern shows up here at the client edge: [Live Queries](/glossary/real-time-live-queries/) are pub/sub where the *query is the topic* — subscribers register a predicate, any write that matches is published to every subscriber over a managed [WebSocket](/glossary/websockets-real-time-sync/) fleet, with permissions checked per subscriber and fan-out handled by the platform, as in the code tabs above. Server-side reactions compose the same way: Cloud Code triggers subscribe to data changes (`afterSave` as a topic subscription in spirit), and scheduled functions drain work published as rows. For heavy inter-service streaming you would still reach for a dedicated broker like Kafka or RabbitMQ — but for the common product cases, broadcast-on-change ships with the backend.
