Skip to content

Quickstart

NexJob needs three things to run: a package reference, a service registration, and a job class. This guide walks you through each step, shows you both ASP.NET Core and Worker Service setups, and links you to runnable reference samples in the repository so you can inspect real, working projects immediately.

1. Install the package

Add the core NexJob package to your .NET 8 project:

dotnet add package NexJob

The core package includes the dispatcher, scheduler, and an InMemory storage provider that is ready to use with no further configuration. For production workloads, add one of the persistent storage providers:

# PostgreSQL
dotnet add package NexJob.Postgres

# SQL Server
dotnet add package NexJob.SqlServer

# Redis
dotnet add package NexJob.Redis

# MongoDB
dotnet add package NexJob.MongoDB

2. Register services in Program.cs

Call AddNexJob() and scan your assembly so NexJob can discover your job classes via dependency injection.

InMemory (default — great for development and testing):

using NexJob;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddNexJob()
               .AddNexJobJobs(typeof(Program).Assembly);

var app = builder.Build();
app.Run();

PostgreSQL (persistent storage for production):

Register the storage provider before calling AddNexJob() so it replaces the InMemory default:

using NexJob;
using NexJob.Postgres;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddNexJobPostgres(
    "Host=localhost;Database=nexjob;Username=postgres;Password=secret");

builder.Services.AddNexJob(options =>
{
    options.Workers = 20;
    options.MaxAttempts = 5;
})
.AddNexJobJobs(typeof(Program).Assembly);

var app = builder.Build();
app.Run();

Tip

Call AddNexJob() exactly once. Calling it multiple times registers duplicate background services and causes undefined behavior.

3. Define a job

Implement IJob for parameterless work or IJob<T> when the job needs structured input. Both interfaces support constructor injection — NexJob resolves your dependencies from the DI container automatically.

Parameterless job (IJob):

public sealed class SendWelcomeEmailJob : IJob
{
    private readonly IEmailService _email;

    public SendWelcomeEmailJob(IEmailService email) => _email = email;

    public async Task ExecuteAsync(CancellationToken ct)
    {
        await _email.SendAsync("user@example.com", "Welcome!", ct);
    }
}

Job with typed input (IJob<T>):

public sealed class SendWelcomeEmailJob : IJob<SendWelcomeEmailInput>
{
    private readonly IEmailService _email;

    public SendWelcomeEmailJob(IEmailService email) => _email = email;

    public async Task ExecuteAsync(SendWelcomeEmailInput input, CancellationToken ct)
    {
        await _email.SendAsync(input.Email, "Welcome!", ct);
    }
}

public sealed record SendWelcomeEmailInput(string Email, string UserName);

Use a record for the input type — it serializes cleanly and is immutable by default.

4. Enqueue the job

Resolve IScheduler from DI and call EnqueueAsync. The dispatcher picks up the job immediately on the same process via the wake-up channel.

var scheduler = app.Services.GetRequiredService<IScheduler>();

// Parameterless job
await scheduler.EnqueueAsync<SendWelcomeEmailJob>(cancellationToken: ct);

// Job with input
await scheduler.EnqueueAsync<SendWelcomeEmailJob, SendWelcomeEmailInput>(
    new SendWelcomeEmailInput("user@example.com", "Jane"),
    cancellationToken: ct);

You can also set a deadline so the job expires automatically if the worker is too busy to start it in time:

await scheduler.EnqueueAsync<SendWelcomeEmailJob, SendWelcomeEmailInput>(
    new SendWelcomeEmailInput("user@example.com", "Jane"),
    deadlineAfter: TimeSpan.FromMinutes(5),
    cancellationToken: ct);

Note

The deadline is checked before execution begins, not during. A job enqueued with deadlineAfter: TimeSpan.FromMinutes(5) that has not started within 5 minutes is marked Expired and never executes.

5. Run the application

Start your application as normal:

dotnet run

NexJob starts the dispatcher as a hosted BackgroundService. Once the job is enqueued, you will see output like:

Hello at 2026-04-08T12:00:00Z

The dispatcher runs on the same process — no separate worker process or sidecar required.

Full minimal example (Worker Service)

The following is a complete, self-contained Worker Service that enqueues one job and waits for shutdown. No ASP.NET Core, no HTTP pipeline — just the job host.

// Program.cs
using NexJob;

var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddNexJob();
        services.AddNexJobJobs(typeof(Program).Assembly);
    })
    .Build();

await host.StartAsync();

var scheduler = host.Services.GetRequiredService<IScheduler>();
await scheduler.EnqueueAsync<HelloJob>();

await host.WaitForShutdownAsync();
// HelloJob.cs
public sealed class HelloJob : IJob
{
    public async Task ExecuteAsync(CancellationToken ct)
    {
        Console.WriteLine($"Hello at {DateTimeOffset.UtcNow}");
        await Task.CompletedTask;
    }
}

Run it:

dotnet run

Expected output:

Hello at 2026-04-08T12:00:00Z

Runnable reference samples

The repository ships with ready-to-run reference architectures covering every NexJob capability. Clone the repo and dotnet run any sample directly.

Sample Focus Port
NexJob.Sample.MinimalApi Dashboard, dead-letter handler, deadline enforcement 5001
NexJob.Sample.WebApi PostgreSQL storage, REST endpoints, .http test files 5002
NexJob.Sample.WorkerService Headless worker with standalone embedded dashboard 5005
NexJob.Sample.ConfiguredRecurring Recurring jobs declared in appsettings.json 5004
NexJob.Sample.RabbitMQ Resilient outbox producer + trigger consumer 5009
NexJob.Sample.Kafka Partitioned outbox publishing + consumer trigger 5010
NexJob.Sample.Reliability Retry, checkpoint, deadline, dead-letter, circuit breaker, job control, health 5011
NexJob.Sample.Providers The same app on PostgreSQL, SQL Server, Redis, MongoDB or InMemory 5012
NexJob.Sample.Storage PostgreSQL read replica, distributed throttle, OTel 5007
NexJob.Sample.CloudTriggers AWS SQS, Azure Service Bus, GCP Pub/Sub, Salesforce 5008

A full local test stack (PostgreSQL 16, Redis 7, RabbitMQ 3.13, Kafka KRaft, SQL Server 2022 and MongoDB 7) is provided in samples/docker-compose.yml.

Next steps

  • Mental Model

    Understand storage-first design, the job state machine, wake-up channel, and crash recovery.

  • Storage Providers

    Configure PostgreSQL, SQL Server, Redis, or MongoDB for production workloads.

  • Scheduling

    Enqueue, schedule with delay, set deadlines, and chain job continuations.

  • Retries & Dead Letter

    Configure exponential backoff and handle exhausted retries gracefully.