Prefix Every Subject With make_subject

Table of Contents

This page defines the NATS pattern Prefix Every Subject With make_subject in the nats pattern language. Every subject that crosses the bus is the configured environment prefix joined to a relative subject, and the join happens inside `ores.nats::service::client` rather than at the call site.

1. Also known as

  • Subject prefixing, namespacing.
  • NATS documentation calls the mechanism subject-based addressing plus account isolation.

2. Context

One built binary runs in several deployments at once — a developer's local environment, a test environment and a production environment — and they may share a NATS cluster or reach separate ones. A subject is a flat global string with no hierarchy beyond its dots.

3. Problem

How does one piece of code address the right stream of messages when the environment it runs in is not known at compile time?

4. Forces

  • A subject written in code must be stable; a subject on the wire must be unique per environment.
  • Two environments on one cluster must not see each other's messages.
  • A request/reply inbox is created by the server and belongs to no environment.
  • A wrong subject fails silently: nothing is listening, so nothing answers.

5. Solution

Code passes a relative subject and the client prepends `nats_options::subject_prefix`. `make_subject()` performs the join, and every publish, request and subscribe method calls it internally, so a caller never builds an absolute subject by hand:

// Relative in code. ores.dev.local2 on the wire.
nats.queue_subscribe(
    ores::iam::messaging::list_accounts_request::nats_subject,
    "ores.iam.service",
    handler);

Two consequences follow, and both are rules:

  • A caller passes what it holds. A relative subject goes straight to the client, which prefixes it. An already-prefixed subject must use the absolute helpers described below, never `make_subject()`.
  • An `_INBOX` subject is exempt. The server creates it for request/reply and a prefix would break the reply path, so `make_subject()` returns it unchanged.

A JetStream stream name is the same prefix under different syntax: `make_stream_name()` replaces the dots with underscores, because a stream name may not contain one.

Need Call
A subject to publish or subscribe on pass the relative subject; the client prefixes it
The subject a stream filter must cover `subjects_below(relative)`, which appends `>`
Filters covering a set of subjects and their children `covering_subjects({…})`
A stream name for this environment `make_stream_name("workflow")`

`subjects_below()` and `covering_subjects()` return absolute subjects for stream filters. Do not pass their result to `subscribe()`, which would prefix it a second time and match nothing.

6. Failure handling

Concern What the pattern does
Errors A subject that matches nothing produces a no-responders signal on a request, which the caller reports; see Bounded Request.
Timeouts Not applicable: prefixing cannot fail at run time.
Retries A prefix mismatch is not retried away; it is a configuration error.
Idempotency Unaffected.
Observability Every producer and consumer logs the prefixed subject, so one grep on the absolute subject finds both sides.

7. Pitfalls

  • Prefixing an `_INBOX` subject by hand. The reply arrives nowhere and the request waits out its whole timeout.
  • A hardcoded absolute subject. It works in one environment and reaches the wrong bus, or none, in another.
  • Passing an absolute subject to `subscribe()`. The prefix is applied twice and the subscription is silent.
  • A stream filter built from the bare relative subject. It misses the subject's children; see Stream Provisioning at Start.

8. Security

The prefix is an addressing mechanism, not an isolation boundary. Real isolation is the NATS account and its permissions, and the tenant a request acts for. See Identity and Access.

9. Resulting context

Benefits
One binary is deployable to any environment with no rebuild, and two environments on one cluster stay independent.
Liabilities
The subject in the log differs from the subject in the code, so a reader must know the prefix is prepended.

10. In ORE Studio

`make_subject()` is in `ores.nats/service/client.cpp` and is called by `publish()`, `request_sync()`, `request()`, `subscribe()`, `subscribe_buffered()`, `queue_subscribe()` and `js_queue_subscribe()`. The prefix itself is resolved at startup into `nats_options::subject_prefix`. `make_stream_name()`, `subjects_below()` and `covering_subjects()` are the absolute forms.

11. Related patterns

12. Sources

13. See also

  • NATS service patterns — the structure note that orders this pattern language, and where to read this page in it.

Emacs 29.3 (Org mode 9.6.15)