Observers Guide
Configure post-mutation side effects, retries, and dead letter queues. Observers
Teams building event-driven microservices typically need to instrument every write path — adding publish calls, handling failures, and managing retries in application code. FraiseQL’s observer system moves this to infrastructure: a TOML config tells the Rust runtime which database writes to publish as events and where to send them.
FraiseQL fires observers after a mutation commits to the database. No Python code is involved at runtime:
Client mutation → Rust runtime executes SQL function (transaction commits) → Rust runtime evaluates [[observers]] config → Matching events published to NATS / Redis / webhook → Subscriber services reactYour Python schema defines what mutations exist. The fraiseql.toml defines what events those mutations emit. No @observer decorator, no Python publish calls.
[observers]backend = "nats"nats_url = "${NATS_URL}"
[[observers]]table = "tb_order"event = "INSERT"subject = "fraiseql.order.created"
[[observers]]table = "tb_order"event = "UPDATE"subject = "fraiseql.order.updated"[observers] (singleton) configures the backend. [[observers]] (array) declares individual rules. Multiple rules can share the same backend.
For teams without a message bus, publish events as outbound HTTP webhooks:
[observers]backend = "webhook"
[[observers]]table = "tb_user"event = "INSERT"
[[observers.actions]]type = "webhook"url_env = "USER_CREATED_WEBHOOK_URL"The url_env key names an environment variable — secrets stay out of config files.
In a federated deployment, each FraiseQL service publishes events to NATS independently. Downstream services subscribe:
[observers]backend = "nats"nats_url = "${NATS_URL}"
# Subscribe to events from order-service[[observers.subscribe]]subject = "fraiseql.order.created"handler = "fn_reserve_inventory"fn_reserve_inventory is a SQL function in the inventory service’s own database. It receives the event payload as a single JSONB argument. No application-layer event bus wrapper required.
Observers move events between services. To deliver real-time updates to browser clients, use GraphQL subscriptions:
@fraiseql.subscription( entity_type="Order", operation="UPDATE")def order_updated(customer_id: fraiseql.ID | None = None) -> Order: """Subscribe to order updates, optionally filtered by customer.""" passClients connect via WebSocket. FraiseQL delivers events over graphql-ws protocol. NATS and subscriptions are complementary: NATS moves events between services; subscriptions push them to end users.
The Microservices Choreography example shows a complete event-driven architecture:
fraiseql.order.created on every new orderfn_reserve_inventoryStart with the Microservices Choreography example or the NATS Event Pipeline example for a working multi-service setup.
For a single-service starting point with webhook observers, use the fraiseql-starter-saas template and add [[observers]] config to fraiseql.toml.
Observers Guide
Configure post-mutation side effects, retries, and dead letter queues. Observers
Observer-Webhook Patterns
Event-driven architecture patterns with observers and outbound webhooks. Observer-Webhook Patterns
Federation + NATS
Cross-service event routing between FraiseQL subgraphs. Federation + NATS Integration
Subscriptions
Real-time push to clients via GraphQL WebSocket subscriptions. Subscriptions
Microservices Example
Complete working example of NATS-choreographed microservices. Microservices Choreography