API

Trax.Api adds a programmatic interface to the scheduling and train execution system. It ships as two NuGet packages — a core library and a GraphQL transport powered by HotChocolate.

The API is designed to run on a separate machine from the scheduler. The two share a database: the API writes work queue entries and manifest updates, the scheduler polls them and dispatches trains. This means the API server doesn't run polling services or background workers — it's a thin HTTP layer over the shared state.

Two Execution Modes

ModeHow It WorksWhen to Use
Queue (delegated)Creates a WorkQueue entry in the database. The scheduler picks it up on its next poll cycle and dispatches it on the scheduler machine.Heavy trains, recurring work, anything that should run on dedicated scheduler infrastructure.
Run (direct)Calls ITrainBus.RunAsync on the API machine (default) or offloads to a remote endpoint via UseRemoteRun(). Either way, the call blocks until completion and returns the train output.Lightweight on-demand trains where you need the result immediately. When using UseRemoteRun(), the API machine doesn't need to run the train code locally.

Trains opt into the GraphQL schema with the [TraxQuery] or [TraxMutation] attributes. Only annotated trains get typed fields generated. EF Core entities can also be exposed directly as paginated, filterable, sortable queries using [TraxQueryModel].

Quick Setup

dotnet add package Trax.Api.GraphQL
var builder = WebApplication.CreateBuilder(args);
 
builder.Services.AddTrax(trax => trax
    .AddEffects(effects => effects
        .UsePostgres(connectionString)
        .AddJson()
    )
    .AddMediator(typeof(Program).Assembly)
);
 
builder.Services.AddTraxGraphQL(); // or: AddTraxGraphQL(g => g.AddDbContext<MyDbContext>())
builder.Services.AddHealthChecks().AddTraxHealthCheck();
 
var app = builder.Build();
 
app.UseTraxGraphQL();  // maps at /trax/graphql — opens Banana Cake Pop IDE in browser
app.MapHealthChecks("/trax/health");
 
app.Run();

Packages

PackageDescription
Trax.ApiCore library — DTOs, health check, shared service registration
Trax.Api.GraphQLHotChocolate schema (queries, mutations, subscriptions)

Trax.Api.GraphQL depends on Trax.Api — you don't need to reference it directly.

Architecture

┌─────────────────────────────┐    ┌─────────────────────────────┐
│         API Server          │    │      Scheduler Server       │
│                             │    │                             │
│  GraphQL endpoint           │    │  ManifestManagerPolling     │
│  ITrainBus (direct runs)    │    │  JobDispatcherPolling       │
│  ITraxScheduler (DB writes) │    │  JobRunner                  │
│  ITrainDiscoveryService     │    │                             │
│  ITrainExecutionService     │    │                             │
└──────────────┬──────────────┘    └──────────────┬──────────────┘
               │                                   │
               └──────────┬───────────────────────┘
                          │
                          ▼
               ┌──────────────────────┐
               │     PostgreSQL       │
               │                      │
               │  manifests           │
               │  work_queue          │
               │  metadata            │
               │  dead_letters        │
               └──────────────────────┘

The API server doesn't need AddScheduler() — it only needs AddMediator() (for train discovery and direct execution) and a data provider (for DB access). The scheduler configuration (AddScheduler) runs on the scheduler machine only.

However, if you want the API to also schedule manifests at startup (like the scheduler does), you can add AddScheduler() on the API machine as well. The polling services can be disabled with configuration if you only want startup seeding.

Health Check

The API includes an ASP.NET Core IHealthCheck that queries the database and reports:

  • Queue depth — work items waiting for dispatch
  • In-progress — currently executing trains
  • Failed (last hour) — recent failures
  • Dead letters — unresolved dead letter entries

In-progress and failed counts are batched into a single database query (via GroupBy) to minimize round-trips on each health check poll.

Returns Healthy when everything looks normal, Degraded when dead letters exist or recent failures exceed a threshold. The same data is available as a GraphQL query for consumers that prefer structured access over the standard health endpoint.

builder.Services.AddHealthChecks().AddTraxHealthCheck();
app.MapHealthChecks("/trax/health");

Authentication & Middleware

Trax doesn't include built-in auth — you add it with standard ASP.NET Core middleware. UseTraxGraphQL accepts a configure callback for applying endpoint conventions like authorization, rate limiting, or CORS:

app.UseTraxGraphQL(configure: endpoint => endpoint
    .RequireAuthorization("AdminPolicy"));

The callback receives an IEndpointConventionBuilder and supports .RequireAuthorization(), .RequireRateLimiting(), .RequireCors(), .AddEndpointFilter<T>(), and any other endpoint convention.

For global auth that applies to everything (including non-Trax routes), use middleware instead:

app.UseAuthentication();
app.UseAuthorization();

Per-Train Authorization

Endpoint-level auth answers "can this user access the API?" For finer control, decorate individual train classes with [TraxAuthorize]:

[TraxAuthorize("Admin")]
[TraxMutation]
public class SensitiveTrain : ServiceTrain<SensitiveInput, Unit>, ISensitiveTrain { ... }

When the API receives a request to run or queue this train, it checks the current user against the policy before executing. Trains without [TraxAuthorize] have no per-train restriction. See the Authorization guide for details.

Named GraphQL Schema

The GraphQL API registers on a named HotChocolate schema ("trax") rather than the default unnamed schema. This means it won't conflict with your own AddGraphQLServer() calls — both can coexist in the same application at different paths.

Sample Projects

The API is demonstrated in three samples, each using a different deployment topology:

  • LocalWorkers (GameServer) — API and scheduler as separate processes. The GraphQL API handles lightweight trains directly and queues heavy work for the scheduler. See samples/LocalWorkers/Trax.Samples.GameServer.Api.
  • DistributedWorkers (EnergyHub) — API, scheduler, and dashboard in a single hub process. The hub schedules and serves GraphQL but offloads execution to separate worker processes. See samples/DistributedWorkers/Trax.Samples.EnergyHub.Hub.
  • EphemeralWorkers (ContentShield) — API with UseRemoteWorkers() dispatches queued mutations directly to an ephemeral Runner via HTTP. No scheduled jobs, no background_job table — purely on-demand, serverless-style execution. See samples/EphemeralWorkers/Trax.Samples.ContentShield.Api.

All follow the trains library pattern — trains live in a shared library, executables are thin wrappers that pick which capabilities to enable.

SDK Reference

> AddTraxGraphQL | UseTraxGraphQL | TraxQuery / TraxMutation | ITrainDiscoveryService | ITrainExecutionService

Next Layer

When you need a monitoring UI for inspecting trains, browsing execution history, and managing manifests from a browser, add Trax.Dashboard.