Skip to main content

Overview

ChangeStreamContext<T> is the main runtime object passed to every handler method (@OnChange, @OnInsert, @OnUpdate, @OnDelete, @OnReplace). It provides access to the event metadata, documents, and runtime actions.

Document access

getFullDocument

Returns the full document after the operation, automatically converted to the requested type using Spring’s MongoConverter.
@OnChange
void handle(ChangeStreamContext<Order> ctx) {
    Optional<Order> order = ctx.getFullDocument(Order.class);
}
For DELETE operations, the full document is not available and the method returns Optional.empty().

getFullDocumentBeforeChange

Returns the document before the operation, automatically converted using Spring’s MongoConverter. This is useful for comparing old vs. new state (e.g., audit logging, status transition tracking).
@OnUpdate
void onStatusChange(ChangeStreamContext<Order> ctx) {
    Optional<Order> before = ctx.getFullDocumentBeforeChange(Order.class);
    Optional<Order> after = ctx.getFullDocument(Order.class);

    if (before.isPresent() && after.isPresent()) {
        log.info("Order {} status: {} → {}",
            after.get().getId(),
            before.get().getStatus(),
            after.get().getStatus());
    }
}
Two prerequisites are required for pre-images to work:
  1. MongoDB 6.0+ — pre-images are not available on earlier versions.
  2. Pre-images must be enabled on the collection — MongoDB does not store pre-images by default. Run this command once per collection:
db.runCommand({
  collMod: "orders",
  changeStreamPreAndPostImages: { enabled: true }
})
Or in Java with Spring’s MongoTemplate:
mongoTemplate.getDb().runCommand(
    new Document("collMod", "orders")
        .append("changeStreamPreAndPostImages", new Document("enabled", true))
);
The @ChangeStream annotation controls how pre-images are requested:
fullDocumentBeforeChangeBehavior if pre-images are not enabled on collection
OFF (default)No pre-image requested. getFullDocumentBeforeChange() always returns Optional.empty().
WHEN_AVAILABLEReturns Optional.empty() silently. Safe for optional use cases.
REQUIREDMongoDB returns an error and the event processing fails. Use when pre-images are mandatory.
@ChangeStream(
    documentType = Order.class,
    fullDocumentBeforeChange = FullDocumentBeforeChangeMode.WHEN_AVAILABLE
)
public class OrderAuditHandler { ... }

getUpdateDescription

For UPDATE operations, returns the list of updated and removed fields.
ctx.getUpdateDescription().ifPresent(desc -> {
    Map<String, Object> updated = desc.getUpdatedFields();
    List<String> removed = desc.getRemovedFields();
});
Custom conversion — By default, getFullDocument(MyType.class) uses Spring’s MongoConverter, which respects your @Field annotations, custom converters registered in MongoCustomConversions, etc.If you need a completely different conversion strategy (e.g. Jackson, Gson), retrieve the raw BSON document and convert it yourself:
@OnChange
void handle(ChangeStreamContext<?> ctx) {
    Document raw = ctx.getFullDocument(Document.class).orElse(null);
    if (raw != null) {
        MyType obj = myCustomMapper.fromBson(raw.toJson());
    }
}

Event metadata

MethodReturnsDescription
getEventId()StringUnique ID generated by FlowWarden for this event
getOperationType()OperationTypeINSERT, UPDATE, REPLACE, DELETE, DROP, INVALIDATE
getStreamName()StringName of the @ChangeStream handler
getCollectionName()StringSource MongoDB collection
getDatabaseName()StringSource MongoDB database
getClusterTime()InstantMongoDB cluster timestamp
getResumeToken()BsonDocumentResume token for checkpointing
getDocumentKey()BsonValueThe _id of the affected document
getAttemptNumber()intCurrent retry attempt (1-based)

Actions

sendToDlq

Manually routes the current event to the Dead Letter Queue.
ctx.sendToDlq("Invalid order total");

saveCheckpointNow

Forces an immediate checkpoint save for the current resume token.
ctx.saveCheckpointNow();

Custom metadata

Attach arbitrary key-value pairs to the event for downstream processing.
ctx.addMetadata("processingRegion", "eu-west-1");

Optional<String> region = ctx.getMetadata("processingRegion", String.class);
Map<String, Object> all = ctx.getAllMetadata(); // unmodifiable