---
title: Reducers
editUrl: https://github.com/Cratis/Chronicle.TypeScript/edit/main/Documentation/reducers.md
description: Reducers are documented in the shared Chronicle docs, with TypeScript examples.
---


Reducers are shared Chronicle read-model behavior. Use the shared reducer docs for the model, lifecycle, filtering, and client-tabbed examples.

- [Reducers](/chronicle/reducers/)
- [Getting started with reducers](/chronicle/reducers/getting-started/)
- [Reducer event processing](/chronicle/reducers/event-processing/)
- [TypeScript client setup](/chronicle/clients/typescript/getting-started/)

## TypeScript client notes

Chronicle calls a reducer method when its name is the camelCase name of the event class, with the event, the current state (`undefined` for the first event), and the event context: `bookBorrowed(event, state, context)` handles `BookBorrowed`. To use another method name, decorate it with `@handles(BookBorrowed)` instead. Import `handles` from `@cratis/chronicle` or `@cratis/chronicle/reducers`; both legacy and standard decorators are supported. Explicit declarations use the same [selection and registration rules as reactors](/chronicle/clients/typescript/reactors/#choosing-a-handler-name): the event constructor must be registered, and each event type has only one handler, whether explicit or conventional. Undecorated helper methods are ignored.

Inherited handlers work, but overriding a method carrying `@handles` requires an explicit `@handles` on the override, even for the same event type. An undecorated override fails registration with `Override '<method>' on '<Derived>' hides @handles(<EventType>) declared on '<Base>'; redecorate the override.` Chronicle also validates methods carrying `@handles` when each reducer instance is created, including instances returned by an activator. Binding the declared method in the constructor is supported; other own properties, including arrow-field overrides, are rejected. Use a method decorated with `@handles` for an override. Conventional handlers retain their existing instance-property behavior. Adding a conventional handler for an event already handled by an inherited method also fails: keep one handler per event type.

Return the new state. Pass the read-model type as the third argument of `@reducer(id, eventSequenceId, ReadModel)` so the client can register its schema.

### Filtering by event source and stream type

Put `@eventSourceType('customer')` and/or `@eventStreamType('shipping')` on the reducer class to fold only events from those types. Import them from `@cratis/chronicle` or `@cratis/chronicle/events`. Both legacy and standard decorators work, and derived reducers inherit filters unless overridden.

The defaults remain `''` for source type (unspecified) and `'All'` for stream type (unrestricted); explicitly declaring those values removes the corresponding inherited restriction. Source and stream filters combine with `@filterEventsByTag` using **AND**, while multiple filter tags match **any** of the declared tags. Observer labels (`@tag`) do not affect delivery. Set `sourceType` and `streamType` in append options; decorating an event class does not set append metadata. Projections do not support these filters.

`ReadModelScenario` rejects a reducer with non-default source or stream filters using `UnsupportedReducerOperation`. Use a kernel-backed specification to verify filtered state instead of an unfiltered in-process fold.

### Activating reducers

By default, the SDK constructs one reducer instance per observation stream. The optional `ChronicleOptions.artifactActivator` also activates reducers: one lease per delivered event batch and separate leases for replay notifications. Its activation context has `kind: ArtifactKind.Reducer`, the owning store/namespace, the partition, event sequence, abort signal, and either the first handled event context or the replay state. `run(callback, invocation)` wraps each handler and receives the current event context and method name (or the replay state for a notification). Existing one-argument `run` implementations still work. `complete()` is awaited once per lease, even after processing fails, before the observation result; a rejected completion fails the partition with `ArtifactCompletionFailed`, retains any processing error, resets the tentative checkpoint, and does not publish tentative read-model state. `dispose()` then runs unconditionally; its errors are only logged. No reducer handler argument changes. State returned by each handler passes to the next handler in the same batch; do not rely on instance fields surviving across batches when using an activator. Reducers should be deterministic during replay: reading mutable external state (including an eventually consistent read model) or appending events from a reducer makes rebuilds unpredictable. Completion failure cannot roll back prior external effects, so make any effects idempotent; see [reactor activation](/chronicle/clients/typescript/reactors/#activating-artifacts) for the lease contract.
