Documentation Index
Fetch the complete documentation index at: https://abxbus.archivebox.io/llms.txt
Use this file to discover all available pages before exploring further.
Context propagation means values you set at request entry (like request_id, user_id, trace/span context) are still available inside event handlers later in the async call chain.
This is commonly used in:
- web servers (FastAPI, Fastify, Express/Nest adapters)
- observability and distributed tracing (OpenTelemetry)
- structured logging/correlation IDs
What this maps to per runtime
- Python uses
ContextVars (contextvars.ContextVar).
- TypeScript (Node/Bun) uses
AsyncLocalStorage.
- Rust uses normal Rust context propagation (
dcontext snapshots are captured at emit time and restored for handlers).
- Go uses
context.Context values captured with EmitWithContext(...) and restored during handler execution.
Python/TypeScript/Rust AbxBus captures ambient context at emit(...) time and restores it when handlers execute. Go propagates the explicit context.Context passed to EmitWithContext(...). In both cases, handler code sees the same request-local values.
Why this matters
Without propagation, handler code often loses request-local state after async boundaries and queue scheduling.
With propagation, event handlers can log/trace as if they were still running in the original request scope.
Python
TypeScript
Rust
Go
from contextvars import ContextVar
from abxbus import EventBus, BaseEvent
request_id: ContextVar[str] = ContextVar('request_id', default='<unset>')
class RequestEvent(BaseEvent):
pass
bus = EventBus('AppBus')
async def handler(_: RequestEvent) -> None:
print(request_id.get())
# req-123
bus.on(RequestEvent, handler)
request_id.set('req-123')
await bus.emit(RequestEvent()).now()
import { AsyncLocalStorage } from 'node:async_hooks'
import { BaseEvent, EventBus } from 'abxbus'
const requestContext = new AsyncLocalStorage<{ requestId: string }>()
const RequestEvent = BaseEvent.extend('RequestEvent', {})
const bus = new EventBus('AppBus')
bus.on(RequestEvent, () => {
console.log(requestContext.getStore()?.requestId)
})
await requestContext.run({ requestId: 'req-123' }, async () => {
await bus.emit(RequestEvent({})).now()
})
use abxbus_rust::{event, event_bus::EventBus};
use dcontext::{enter_named_scope, get_context, set_context, try_initialize, RegistryBuilder};
use futures::executor::block_on;
event! {
struct RequestEvent {
event_result_type: (),
}
}
let mut registry = RegistryBuilder::new();
registry.register::<String>("request_id");
let _ = try_initialize(registry);
let bus = EventBus::new(Some("AppBus".to_string()));
bus.on(RequestEvent, |_event: RequestEvent| async move {
println!("{}", get_context::<String>("request_id"));
// req-123
Ok(())
});
let _scope = enter_named_scope("request");
set_context("request_id", "req-123".to_string());
let event = bus.emit(RequestEvent { ..Default::default() });
block_on(event.now());
package main
import (
"context"
"fmt"
abxbus "github.com/ArchiveBox/abxbus/abxbus-go/v2"
)
type contextKey string
func main() {
requestIDKey := contextKey("request_id")
bus := abxbus.NewEventBus("AppBus", nil)
bus.On("RequestEvent", "handler", func(event *abxbus.BaseEvent, ctx context.Context) (any, error) {
fmt.Println(ctx.Value(requestIDKey))
// req-123
return nil, nil
}, nil)
ctx := context.WithValue(context.Background(), requestIDKey, "req-123")
if _, err := bus.EmitWithContext(ctx, abxbus.NewBaseEvent("RequestEvent", nil)).Now(); err != nil {
panic(err)
}
}
Web server style examples
These patterns are typical in frameworks where each incoming request gets a request-local context object.
Python
TypeScript
Rust
Go
# FastAPI-style shape (conceptual)
request_id.set(incoming_request.headers.get('x-request-id', 'generated-id'))
await bus.emit(RequestEvent()).now()
# handlers can still read request_id.get()
// Fastify-style shape (conceptual)
await requestContext.run({ requestId: req.id }, async () => {
await bus.emit(RequestEvent({})).now()
})
// handlers can still read requestContext.getStore()
let _scope = enter_named_scope("request");
set_context("request_id", incoming_request_id.to_string());
let event = bus.emit(RequestEvent { ..Default::default() });
block_on(event.now());
// handlers can still read get_context::<String>("request_id")
// net/http-style shape (conceptual)
ctx := context.WithValue(req.Context(), requestIDKey, req.Header.Get("x-request-id"))
if _, err := bus.EmitWithContext(ctx, abxbus.NewBaseEvent("RequestEvent", nil)).Now(); err != nil {
panic(err)
}
// handlers can still read ctx.Value(requestIDKey)
Browser runtime note
AsyncLocalStorage is a Node/Bun API and is not available in browser runtimes.
In browsers:
- AbxBus still works normally for events.
- ambient async context propagation via
AsyncLocalStorage is not available.
- pass correlation/tracing fields explicitly in event payloads when you need that metadata.
See Supported Runtimes for runtime compatibility details.
Go does not have ambient AsyncLocalStorage/ContextVar state; pass the request context at emit time with bus.EmitWithContext(...) or event.EmitWithContext(...) and AbxBus propagates that context.Context into handlers and awaited child events.