Configuring Effect Providers
Effect providers handle side effects as a train runs: database writes, logging, serialization. Each provider is independent: add or remove any of them without changing your train code. For the conceptual background, see Effect overview.
Database Persistence (Postgres or InMemory)
Use when: You need to query train history, audit execution, or debug production issues.
// Production
services.AddTrax(trax => trax
.AddEffects(effects => effects
.UsePostgres("Host=localhost;Database=app;Username=postgres;Password=pass")
)
);
// Testing
services.AddTrax(trax => trax
.AddEffects(effects => effects
.UseInMemory()
)
);
This persists a Metadata record for each train execution containing:
- Train name and state (Pending -> InProgress -> Completed/Failed)
- Start and end timestamps
- Serialized input and output
- Exception details if failed
- Parent train ID for nested trains
See Data Persistence for the full breakdown of both backends, what gets persisted, and DataContext logging.
JSON Effect (AddJson)
Use when: Debugging during development. Logs train state changes to your configured logger.
services.AddTrax(trax => trax
.AddEffects(effects => effects
.AddJson()
)
);
This doesn’t persist anything. It just logs. Useful for seeing what’s happening without setting up a database.
See JSON Effect for how change detection works.
Parameter Effect (SaveTrainParameters)
Use when: You need to store train inputs/outputs in the database for later querying or replay.
services.AddTrax(trax => trax
.AddEffects(effects => effects
.UsePostgres(connectionString)
.SaveTrainParameters() // Serializes Input/Output to Metadata
)
);
Without this, the Input and Output columns in Metadata are null. With it, they contain JSON-serialized versions of your request and response objects. You can control which parameters are saved:
.SaveTrainParameters(configure: cfg =>
{
cfg.SaveInputs = true;
cfg.SaveOutputs = false; // Skip output serialization
})
This configuration can also be changed at runtime from the dashboard’s Effects page.
See Parameter Effect for details, custom serialization options, and configuration properties.
Junction Logger (AddJunctionLogger)
Use when: You want structured logging for individual junction executions inside a train.
services.AddTrax(trax => trax
.AddEffects(effects => effects
.AddJunctionLogger(serializeJunctionData: true)
)
);
This hooks into EffectJunction (not base Junction) lifecycle events. Before and after each junction runs, it logs structured JunctionMetadata containing the junction name, input/output types, timing, and Railway state (Right/Left). When serializeJunctionData is true, the junction’s output is also serialized to JSON in the log entry.
Requires junctions to inherit from EffectJunction<TIn, TOut> instead of Junction<TIn, TOut>. See EffectJunction vs Junction.
See Junction Logger for the full JunctionMetadata field reference.
Junction Progress & Cancellation Check (AddJunctionProgress)
Use when: You need per-junction progress visibility in the dashboard and/or the ability to cancel running trains from the dashboard (including cross-server cancellation).
services.AddTrax(trax => trax
.AddEffects(effects => effects
.AddJunctionProgress()
)
);
This registers two junction-level effect providers:
- CancellationCheckProvider queries the database for
Metadata.CancellationRequestedbefore each junction. Iftrue, throwsOperationCanceledException, which maps toTrainState.Cancelled. - JunctionProgressProvider writes the junction name and start time to
Metadata.CurrentlyRunningJunctionandMetadata.JunctionStartedAtbefore each junction. After the junction, clears both columns.
The cancellation check runs first so a cancelled train never writes progress columns for a junction that won’t execute. Requires junctions to inherit from EffectJunction<TIn, TOut>.
See Junction Progress for the dual-path cancellation architecture and dashboard integration.
Lifecycle Hooks (AddLifecycleHook)
Use when: You want side effects to fire on train state transitions (notifications, metrics, real-time updates) without coupling your train code to those concerns.
services.AddTrax(trax => trax
.AddEffects(effects => effects
.AddLifecycleHook<SlackNotificationHook>()
)
);
Lifecycle hooks implement ITrainLifecycleHook and fire at four points: OnStarted, OnCompleted, OnFailed, OnCancelled. Unlike effect providers, hook exceptions are caught and logged, never propagated. A failing Slack webhook will never cause a train to fail.
The Trax.Api.GraphQL package includes a built-in hook (GraphQLSubscriptionHook) that publishes lifecycle events to GraphQL subscriptions over WebSocket. It’s automatically registered by AddTraxGraphQL(). Only trains decorated with [TraxBroadcast] have their events published.
Combining Providers
Providers compose. A typical production setup:
services.AddTrax(trax => trax
.AddEffects(effects => effects
.UsePostgres(connectionString) // Persist metadata
.SaveTrainParameters() // Include input/output in metadata
.AddJunctionLogger(serializeJunctionData: true) // Log individual junction executions
.AddJunctionProgress() // Junction progress + cancellation check
)
.AddMediator(assemblies) // Enable train discovery
);
A typical development setup:
services.AddTrax(trax => trax
.AddEffects(effects => effects
.UseInMemory() // Fast, no database needed
.AddJson() // Log state changes
.AddJunctionLogger() // Log junction executions
)
.AddMediator(assemblies)
);