direct://¶
Synchronous in-process messaging between two named endpoints in the same Helix context. The producer call lands in the consumer's processor on the same goroutine — no queue, no copy — so it's the fastest way to fan a route into reusable sub-routes.
Reach for direct:// when you want a named in-process entry point
(e.g. a logic route that's invoked from multiple places, a sub-route
called by an agent tool, an A2A handler). Use seda://
instead when the call should be asynchronous.
URI shape¶
<name> is an identifier scoped to the running Helix context. Identical
names refer to the same endpoint — a to: direct://my-logic resolves
the consumer registered on from: direct://my-logic. Only one consumer
per name is permitted.
Parameters¶
| Param | Type | Default | Side | Description |
|---|---|---|---|---|
name |
path | — | both | Endpoint identifier (the path portion of the URI). |
block |
boolean | true |
producer | Block until a consumer becomes active or the timeout elapses. If false, returns ConsumerNotAvailableError immediately when no consumer is registered. |
timeout |
duration | 30s |
producer | Max time the producer waits for a consumer when block=true. Accepts Go durations (30s, 500ms) or integer milliseconds (30000). |
failIfNoConsumers |
boolean | true |
producer | Return ConsumerNotAvailableError when no consumer is registered. When false the message is silently dropped (logged at DEBUG). |
synchronous |
boolean | false |
both | Accepted for cross-framework compatibility; Helix is synchronous by default. |
Body shape¶
Producer: any value — passed through unchanged on the same exchange.
Consumer: receives the producer's In message verbatim. Out replies on
InOut exchanges flow back to the caller via the same exchange.
Headers¶
direct:// itself sets no headers. Whatever's on the exchange when the
producer calls it is what the consumer sees.
Examples¶
A reusable sub-route invoked from multiple places:
routes:
- id: enrich-customer
from: direct://enrich-customer
steps:
- to: postgres://customers/findOne?query=SELECT * FROM customers WHERE id = $1
- setHeader:
name: enriched-at
starlang: now()
- id: order-pipeline
from: kafka://orders?groupId=svc
steps:
- to: direct://enrich-customer # InOnly call
- to: postgres://orders/insert
InOut request/reply over direct://:
- id: api
from: http-server://0.0.0.0:8080/v1/customers
steps:
- to: direct://enrich-customer # pattern InOut: caller's body is replaced by reply
Tolerant fan-out (drop if no consumer yet):
Behavior notes¶
- One consumer per name. Registering a second consumer on the same
name returns an error from
Component.addConsumer. Useseda://withmultipleConsumers=trueif you need pub/sub. - Lifecycle. A consumer registers in
Start(), unregisters inStop().Suspend()temporarily unregisters (producers block / fail per theblockandfailIfNoConsumerssettings) andResume()re-registers without losing the processor binding. - Producer cache. Each
Producercaches the resolved consumer pointer keyed by a component-level state counter so the hot path doesn't take the consumer-registry mutex. synchronousis a no-op. Helix routes are synchronous by default; the parameter is accepted for compatibility.- Error type. When
failIfNoConsumers=true, the producer returns a*direct.ConsumerNotAvailableError. Usedirect.IsConsumerNotAvailable(err)to test it;errors.Asalso works.
Source¶
pkg/component/direct/direct.go— Component, Endpoint, Producer, Consumer, error types.
See also¶
- Asynchronous queues:
seda:// - Reusable logic routes guide: see
docs/guides/