NATS Subject Wildcards: Choosing Between * and > for Hierarchical Routing
NATS subject wildcards control message fan-out. Learn when to use single-level * versus multi-level >, with CLI examples and routing limits.
03 Sept 2025, 21:21 UTC

The Problem: Flat Subjects and Over-Subscription
Imagine a logistics service that publishes every event to a single subject, events. Every service that cares about logistics receives every event—inventory updates, delivery scans, and billing alerts all flow to every consumer. Each service must filter locally, wasting CPU and network bandwidth. The opposite extreme is just as bad: subscribing to > to catch everything turns a subscriber into an expensive audit log and makes the NATS server evaluate more subject branches for every message.
The useful takeaway is that subject design is a routing decision. In NATS, a subject is the named channel a message is published to. A publisher sends messages to a subject, and a subscriber declares interest in one or more subjects. Choosing how to name subjects and which wildcards to use determines how efficiently messages fan out.
The Thesis: Match One Level with *, Catch the Rest with >
NATS supports two wildcard tokens in subscription subjects. The asterisk (*) matches exactly one subject level—the text between two dots. The greater-than sign (>) matches one or more remaining levels and must appear as the final token. Use * when you want precise, bounded fan-out. Reserve > for catch-all cases such as audit trails or debugging, where you intentionally want every message beneath a prefix.
How NATS Subject Matching Works
Single-level wildcard (*)
A subscription to orders.*.create matches orders.us.create and orders.eu.create, but not orders.create (too few levels) and not orders.us.v2.create (too many levels). This keeps routing predictable because the server only checks one token position.
Multi-level wildcard (>)
A subscription to orders.us.> matches orders.us.create, orders.us.ship, and orders.us.v2.create. Because > consumes the rest of the subject, it must be the last token; orders.>.create is invalid.
Worked Example: Filtering Order Events
Run these commands in separate local terminals. This assumes the official NATS CLI tools and a NATS Server are installed; no special permissions are required for a local loopback server.
# Terminal 1: start a local NATS server on the default port
nats-server -p 4222
# Terminal 2: subscribe to exactly one region-level token before "create"
nats-sub 'orders.*.create'
# Terminal 3: subscribe to everything under orders.us
nats-sub 'orders.us.>'
# Terminal 4: publish events
nats-pub orders.us.create '{"order_id":101,"action":"created"}'
nats-pub orders.eu.create '{"order_id":102,"action":"created"}'
nats-pub orders.us.ship '{"order_id":101,"action":"shipped"}'
nats-pub orders.create '{"order_id":103,"action":"created"}'
The subscriber on Terminal 2 should receive the first two messages only. The subscriber on Terminal 3 should receive the first and third messages only. Both should print the orders.us.create message because the subjects overlap. If a subscriber is not running when a message publishes, that message is lost—core NATS does not store messages by default.
Risk: do not use > as a general-purpose subscription in high-throughput services unless you truly need every descendant subject. The server must walk more of the subject tree for each publish, which adds routing overhead at scale.
Limitations and Verification
Plain core NATS is best-effort and ephemeral. JetStream streams and consumers add durable retention and acknowledgment-based delivery. In clustered setups, network partitions can cause duplicate deliveries, so design message handlers to be idempotent.
Practical verification: after starting the subscribers above, publish to orders.apac.create. Terminal 2 should receive it because * matches apac; Terminal 3 should not because the subject lacks the us prefix. Then publish to orders.us.v2.create. Terminal 2 should ignore it, while Terminal 3 should receive it. If the results differ, check that the wildcard is the last token and that there are no extra spaces in the subject string.
Closing
Model your subjects as a hierarchy, such as region.service.action, and subscribe with * for bounded, predictable distribution. Use > sparingly for audit or debugging consumers. Validate the behavior with the CLI examples above before wiring it into application code, and remember that messages published with no matching subscriber are gone unless you enable JetStream persistence.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.