OnReceiveEvents handler is a custom module hook that allows you to intercept and process events received from
supported message providers before they are delivered to GraphQL subscription clients.
This handler is called whenever a batch of events is received from a provider, giving you the opportunity to filter, transform, enrich,
or validate events before they reach your subscribers.
This handler is particularly useful for:
- Event filtering: Remove unwanted events based on custom logic
- Data transformation: Modify event payloads to match client expectations
- Event enrichment: Add additional data to events from external sources
- Authentication and authorization: Filter events based on user permissions
- Monitoring and analytics: Log or track events for observability
If there is no active subscription this handler is not executed, even if new messages arrive at the provider.
This is because the Router will not listen for messages on the provider topic/queue until at least one
client subscribes to a particular subscription.
Handler Interface
In order to use theOnReceiveEvents handler you need to create a Custom Module which implements
the StreamReceiveEventHandler interface.
Asynchronous Execution and Performance Considerations
TheOnReceiveEvents handler is executed asynchronously for each active subscription when events are received from the provider.
To control resource usage and prevent overwhelming your system, you can configure the maximum number of concurrent handlers using
the max_concurrent_handlers configuration option.
ctx.Context(), which is cancelled in such situations.
You can use this context to abort any long-running operations:
The router does not abort the handler when the context is cancelled. Instead, it proceeds to receive the next batch of events from the provider.
In this case, events may be delivered out of order because long-running handlers are still processing the previous batch.
Error Handling
When theOnReceiveEvents handler returns an error, the router takes the following actions:
- Send Returned Events: Alongside the error you can return events, if you wish to sent them to the client prior connection closing
- Subscription Closure: The affected subscription is immediately closed for the client that encountered the error
- Error Logging: The error is logged by the router with details about the subscription, provider, and field name
- Error Deduplication: If multiple subscriptions experience the same error for the same events, the router deduplicates the error messages in the logs to prevent spam
- No Error Propagation: The error is not sent directly to the GraphQL client - the subscription simply closes
The error gets logged by the router but it won’t be send to the client.
From the view of the client the subscription closes server-side without a reason. We are working on a solution for this.
Usage Example
Complete Custom Module with Event Bypass
The following example contains a complete Custom Module implementation, including handler registration, with a handler that will simply bypass events unchanged. This is not useful on it’s own but demonstrates how to register yourOnReceiveEvents Custom Module.
Restrict Handler to run on certain subscriptions and providers
Most of the time you want your hook to only deal with a certain subscription.The
OnReceiveEvents Handler is run for every subscription configured for Cosmo Streams.
You can access the name of the subscription you care for and return early if it’s not the right one.
Filter out events based on clients authentication token claim
You can usectx.Authentication() to access authentication data, such as tokens, if available.
Based on that you can filter events, if the token misses the proper claim.
Filter out events based on message metadata
Certain providers enrich their messages with metadata accessible by the Router. Kafka and NATS, for example, have the option to add headers to messages. Here’s an example that filters out all messages coming from a Kafka instance where a header indicates it’s not meant for GraphQL subscriptions.Transform events
You can change event data. For example you could change the id of the entity you want to resolve for a client.Cat entity with id 3, no matter what the event originally references.