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 — Before each junction, queries the database for
Metadata.CancellationRequested. Iftrue, throwsOperationCanceledException, which maps toTrainState.Cancelled. - JunctionProgressProvider — Before each junction, writes the junction name and start time to
Metadata.CurrentlyRunningJunctionandMetadata.JunctionStartedAt. 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)
);SDK Reference
> UsePostgres | UseInMemory | AddJson | SaveTrainParameters | AddJunctionLogger | AddJunctionProgress | AddLifecycleHook | AddMediator