This episode covers subject hierarchy and event-driven naming best practices, then the difference between the * and > wildcards along with their implications for permissions, subscriptions, and matching conflicts.

Episode 3 got you connected. Episode 4 sharpens the art of routing: subjects are the language of NATS, and you need to be fluent in it. The way you name subjects determines how easy your system is to understand, extend, and secure.
We'll cover subject hierarchy, event-driven naming best practices, the difference between the * and > wildcards, and how both affect permissions and subscriptions. This is the foundation that will determine security in episodes 12-13.
A subject is a dot-separated string that forms a natural hierarchy:
orders
└── created
└── eu
└── de
└── cancelled
payments
└── chargedorders.created.eu.de is the most specific subject; a orders.> subscriber receives everything. This hierarchy is purely a naming convention — the server doesn't store a tree — but it gives you enormous routing power.
A very common pattern: subjects start with a verb to model requests. For example auth.login, users.get, orders.process. A *.get subscriber can serve all read requests. This pattern pairs perfectly with request-reply, which we'll discuss in episode 5.
nats pub orders.created "pesanan-1"
nats pub orders.created.eu "pesanan-2"
nats pub orders.cancelled "pesanan-3"nats pub orders.created.eu "pesanan-2" sends to a more specific subject; an orders.created subscriber only sees pesanan-1, while an orders.> subscriber sees all three.
Good naming follows a domain + entity + action pattern. This helps teams understand semantics from the name alone:
orders.created, orders.shipped, orders.delivered — the order lifecycle.payments.charged, payments.refunded — payment events.user.registered — a user registration event.Use past tense for events that have already happened and present tense for requests still in flight. This consistency makes searching and permissioning easier.
A few golden rules used by the community:
Warning
Subjects are the public API between services. Changing a subject name can break existing subscribers. Treat your subject schema like a contract — design it carefully from the start.
* and >NATS's two wildcards have a strict difference:
* matches exactly one token at its position.> matches one or more tokens and is only valid at the end of a subject.nats sub 'orders.*'
nats sub 'orders.>'nats sub 'orders.*' receives orders.created but rejects orders.created.eu (two tokens). nats sub 'orders.>' receives both. Wildcards may only be used on the subscription or permission side, never when publishing.
Wildcards can be combined for precise patterns:
orders.*.eu # orders.created.eu, orders.cancelled.eu
orders.>.de # orders.created.eu.de, orders.cancelled.de
*.created # orders.created, payments.createdThe orders.*.eu pattern matches all order events for the eu region with exactly one level of action. This precision matters: an overly broad wildcard can catch messages you don't want.
Wildcards aren't only for subscriptions — they're used in per-user permissions inside the authorization block. The difference between * and > becomes a security matter:
authorization {
users = [
{ user: alice, permissions: {
subscribe: { allow: ["orders.>"], deny: ["orders.internal"] }
publish: { allow: ["orders.created", "payments.>"] }
}}
]
}The authorization block above gives alice the right to subscribe to all orders subjects except those starting with orders.internal, and the right to publish only to orders.created plus all payments subjects. Wildcards define the safe boundaries of access.
Watch out for conflicts: if alice is allowed orders.> but denied orders.internal, then a message on orders.internal.something is rejected because the more specific deny rule wins. The server evaluates allow and deny, and the more precise deny wins the conflict.
def boleh_subscribe(user, subject):
if subject.startswith("orders.internal"):
return "TOLAK"
if subject.startswith("orders"):
return "IZIN"
return "TOLAK"The boleh_subscribe(user, subject) function above simplifies NATS's permission logic: a specific deny rule beats a broad allow. This is the principle to remember when designing multi-tenancy in episode 12.
Episode 4 taught the language of NATS routing: the tree-shaped subject hierarchy, event-driven naming best practices with the domain-entity-action pattern, the difference between the one-token * and rest-of-tokens > wildcards, and their implications for permissions and matching conflicts.
Key takeaways:
* matches one token, > matches the rest of the tokens and only at the end.deny rule wins conflicts with allow.In episode 5 next, we'll discuss request-reply & queue groups — using nats request for RPC-style patterns between microservices, handling timeouts, and load balancing between consumers with queue groups, compared with competing consumers. This is where NATS starts to feel like the backbone of microservices.