tap
tap(destination: Destination<Current, unknown> | CallableDestination<Current, unknown>): RouteBuilder<Current>
Execute side effects without changing the exchange. The tap operation is async fire-and-forget - it runs in the background and never blocks the main route. Return values are ignored.
The tap receives a deep copy of the exchange with:
- New exchange ID
- Cloned body and headers
- Correlation ID preserved for traceability back to parent exchange
// Simple function-based tapping
.tap(log()) // Built-in logging
.tap((exchange) => console.log('Processing:', exchange.body))
.tap(async (exchange) => await sendNotification(exchange.body))
// Multiple taps for different concerns
.tap(analytics())
.tap(monitoring())
.to(primaryDestination)
Key behaviors:
- Async fire-and-forget: Main route continues immediately without waiting
- Exchange snapshot: Tap receives a deep copy with new ID and correlation metadata
- Return values ignored: Any value returned by the tap destination is discarded
- Error isolation: Errors in tap are emitted to the route error handler but don't halt the main exchange (already fire-and-forget)
- Lifecycle aware: Routes and context wait for all taps to complete during shutdown via
drain() - Perfect for: Logging, auditing, notifications, analytics, monitoring
Lifecycle:
- Routes complete without waiting for taps
- Taps are tracked by the route and waited for during
drain() context.stop()automatically callscontext.drain()to wait for all tap jobs- Ensures all async work finishes before shutdown completes