WJb Docs - Intermediate

Learn workflows, persistence, scheduling, and production-ready patterns.

πŸš€ Start Here
🧩 Mental Model
🌊 Building Workflows
πŸ’₯ Failures & Control
πŸ‘€ Observation & Debugging
πŸ›  Practical Patterns
βœ… Working Examples
➑️ What’s Next

⚑ Create & Register Actions

Actions contain the business logic of your application.

Every unit of work in WJb is an Action.


Creating an Action

Inherit from JobAction:

public sealed class SendEmailAction : JobAction<EmailInput>
{
    public override async Task<IActionResult> ExecuteAsync(
        EmailInput input, CancellationToken ct)
    {
        // send email logic here

return await CompleteAsync(); } }

The generic parameter TInput defines the strongly typed payload the action expects.


Input Model

public sealed class EmailInput
{
    public string To { get; init; } = string.Empty;
    public string Subject { get; init; } = string.Empty;
    public string Body { get; init; } = string.Empty;
}

WJb automatically converts the job payload into this type.


Action Name

You can give the action an explicit name:

[ActionName("send-email")]
public sealed class SendEmailAction : JobAction<EmailInput>
{
    // ...
}

This name is used when enqueueing jobs and when creating next commands.


Registering Actions

Use WJbBuilder:

var wjb = WJbBuilder.Create(store, cfg =>
{
    cfg.AddAction<SendEmailAction>();          // uses ActionName or type name
    // or
    cfg.AddAction<SendEmailAction>("send-email");
});

You can also register multiple actions or load them from JSON / a definition store.


Enqueueing a Job

Once registered, you can start a job:

await wjb.EnqueueAsync("send-email",
    new EmailInput
    {
        To = "user@test.com",
        Subject = "Hello",
        Body = "Welcome!"
    });

or using the type:

await wjb.EnqueueAsync<SendEmailAction>(
    new EmailInput { ... });

Dependency Injection

Actions support constructor injection:

public sealed class SendEmailAction(IEmailService email)
    : JobAction<EmailInput>
{
    public override async Task<IActionResult> ExecuteAsync(
        EmailInput input, CancellationToken ct)
    {
        await email.SendAsync(input.To, input.Subject, input.Body, ct);

return await CompleteAsync(); } }

Register the required services with the same builder or the DI container.


Minimal Complete Example

[ActionName("send-email")]
public sealed class SendEmailAction : JobAction<EmailInput>
{
    public override Task<IActionResult> ExecuteAsync(
        EmailInput input, CancellationToken ct)
    {
        Console.WriteLine($"Sending email to {input.To}");
        return CompleteAsync();
    }
}

// Registration var wjb = WJbBuilder.Create(store, cfg => { cfg.AddAction<SendEmailAction>(); });

// Usage await wjb.EnqueueAsync("send-email", new EmailInput { To = "user@test.com" });


Best Practices

  • Keep one clear responsibility per action
  • Prefer small, focused input models
  • Always pass the CancellationToken
  • Use [ActionName] for stable keys
  • Prefer constructor injection over service locator

Key Point

An action is just a class that receives input, does work, and returns a result.

Everything else in WJb builds on this simple foundation.

An unhandled error has occurred. Reload πŸ—™

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please retry or reload the page.