Skip to main content
Echoes in Orionjs provide a structured way to implement event-driven architecture and handle inter-service communication. Using the @Echoes() and @EchoRequest() or @EchoEvent() decorators, you can easily create handlers for both synchronous requests and asynchronous events.

Standalone core and Orion integration

@orion-js/echoes is framework-independent. Its required dependency graph does not include @orion-js/schema, @orion-js/services, @orion-js/http, @orion-js/logger, or any other Orion runtime package. Orion applications keep their existing behavior through @orion-js/echoes-orion. It connects Echoes to Orion schemas, dependency injection, async logger context, HTTP routes, and Orion error classes:
Import the adapter once, before modules that declare Echoes controllers:
Applications using @orion-js/components do not need this explicit import because Components installs the adapter automatically.
Schema execution is still runtime behavior, not just TypeScript inference. For every handler, Echoes cleans and validates params before calling resolve, then cleans returns before sending the result. The Orion adapter delegates those operations to @orion-js/schema, so existing custom cleaners, defaults, validations, and ValidationError values keep working.

Creating Echo Controllers

An echoes controller is a class decorated with @Echoes() that contains methods decorated with @EchoRequest() or @EchoEvent():

Echo Request Handlers

Use the @EchoRequest() decorator with the createEchoRequest() function to define methods that handle synchronous requests from other services:

Echo Event Handlers

Use the @EchoEvent() decorator with the createEchoEvent() function to define methods that process asynchronous events:

Event Decorator Options

The createEchoEvent() function accepts options similar to createEchoRequest():

Making Requests

To make a request to another service:

Publishing Events

To publish an event for other services to consume:

Starting the Echoes Service

To enable echoes in your application, you need to configure and start the echoes service:

Using Echoes without Orion

A standalone service can use a SimpleSchema-compatible object directly. Echoes recognizes objects with clean() and validate() methods and executes both at runtime. typedEchoesSchema() adds the value type when the schema library does not expose it in a form TypeScript can infer:
For synchronous Echoes requests, connect the framework-neutral receiver to the HTTP server of your choice:
Kafka and Pulse are optional transports and are loaded only when selected. Install kafkajs for Kafka, or @orion-js/pulse with its MongoDB peer dependency for Pulse. A service that only uses synchronous requests loads neither event transport.

Incremental migration from Kafka to Pulse

Echoes can listen through Kafka and Pulse at the same time while publishing through exactly one transport. This allows the migration to happen in three independent deployments, without dual-publishing an event. Install Pulse and its MongoDB peer dependency in the application:

1. Prepare every listener

Keep the existing top-level Kafka configuration and add Pulse under events. Deploy this version to every instance before changing any publisher:
At this point, handlers accept events from both systems, but publish() still sends only to Kafka. The event handler API does not change. The Pulse transport uses a MongoDB application pool of at most five connections per service instance by default. Set events.pulse.maxPoolSize when a specific service needs a different limit.

2. Switch publishers

Once every listener is running the dual-consumer configuration, change only publishTo:
This can be rolled out gradually. Old application instances continue publishing to Kafka and new instances publish to Pulse; the listener fleet consumes both. For a controlled switch, publishTo can also come from an environment flag:

3. Remove Kafka consumption

After every publisher uses Pulse and Kafka has no pending events for the service, stop consuming from Kafka:
The legacy client, consumer, and producer options can then be removed from that application. If only Pulse is configured, Echoes defaults both consumption and publishing to Pulse.
Echoes never publishes the same call to both transports. During a rolling publisher deployment, Kafka and Pulse form two independent ordered streams, so they do not provide a shared total order. Topics that require strict ordering should use a coordinated publisher cutover after draining Kafka.
Pulse is an optional peer dependency and is loaded only when configured. Applications that stay on Kafka do not need to install or initialize it.

Error Handling

Echoes automatically handles errors in request and event handlers:

Custom Error Types

Orionjs handles special error types appropriately:
  • UserError: For expected application errors
  • ValidationError: For data validation errors
Those errors will be automatically propagated back to the requester.

Type Safety

Using TypeScript with schema inference, you can ensure type safety for your echo handlers:

Best Practices

  1. Organize by Domain: Group related echo handlers in the same controller class.
  2. Leverage Dependency Injection: Use @Inject(() => Service) to access repositories and services.
  3. Keep Methods Focused: Each echo handler should have a clear, single responsibility.
  4. Use Strong Typing: Define parameter and return types with schemas and infer TypeScript types.
  5. Handle Errors Gracefully: Catch and properly categorize errors.
  6. Idempotent Handlers: Design event handlers to be idempotent (safe to process the same event multiple times).
  7. Timeout Configuration: Set appropriate timeouts for requests based on expected execution time.
  8. Security: Use the shared key to secure inter-service communication.
  9. Service Discovery: Keep the service registry updated when adding new services.
  10. Monitoring: Implement proper logging and monitoring for echo handlers.