Queries

Queries are organized into two groups under the root Query type:

type Query {
  discover: DiscoverQueries!
  operations: OperationsQueries!
}
  • discover: auto-generated typed query fields for trains annotated with [TraxQuery]
  • operations: predefined operational queries: health status, registered trains, manifests, manifest groups, and execution history

Discover Queries (Auto-Generated)

Trax auto-generates strongly-typed query fields for trains that opt in with [TraxQuery]. Only trains with this attribute appear under discover.

Each whitelisted query train gets a single field named after the train (no prefix). The field accepts a strongly-typed input argument and returns the train’s output type directly. Trains with Namespace set are grouped under a sub-namespace (e.g. discover { players { lookupPlayer } }).

Naming Convention

The query field names are derived from the train’s service interface name (or overridden via [TraxQuery(Name = "...")]):

  1. Strip the I prefix
  2. Strip the Train suffix
  3. Use the result as the field name (lowercase first letter)

For example, ILookupPlayerTrain produces lookupPlayer.

Example

Given a train annotated with [TraxQuery]:

public record LookupPlayerInput
{
    public required string PlayerId { get; init; }
}

public record LookupPlayerOutput
{
    public required string PlayerId { get; init; }
    public required int Rank { get; init; }
}

The schema exposes:

query {
  discover {
    lookupPlayer(input: { playerId: "player-42" }) {
      playerId
      rank
    }
  }
}

Query trains with typed output

When a query train has a non-Unit output type, the output type is returned directly (not wrapped in a response type):

type DiscoverQueries {
  lookupPlayer(input: LookupPlayerInput!): LookupPlayerOutput!
}

Query trains with Unit output

When a query train has Unit output, it returns a response with the execution metadata:

Field Type Description
metadataId Long! Metadata ID of the completed execution

Operations Queries

health

Returns the current health status of the Trax scheduler system. This is the same data reported by the ASP.NET IHealthCheck at /trax/health, exposed as a structured GraphQL type.

query {
  operations {
    health {
      status
      description
      queueDepth
      inProgress
      failedLastHour
      deadLetters
    }
  }
}

Returns: HealthStatus!

HealthStatus fields

Field Type Description
status String! "Healthy" or "Degraded"
description String! Human-readable summary
queueDepth Int! Work items with status Queued
inProgress Int! Executions with TrainState.InProgress
failedLastHour Int! Failed executions in the last hour
deadLetters Int! Dead letters with status AwaitingIntervention

Status is Degraded when deadLetters > 0 or failedLastHour > 10.


trains

Returns every train registered in the DI container, including a runtime-generated input schema describing each property on the input type.

query {
  operations {
    trains {
      serviceTypeName
      implementationTypeName
      inputTypeName
      outputTypeName
      lifetime
      inputSchema {
        name
        typeName
        isNullable
      }
    }
  }
}

Returns: [TrainInfo!]!

TrainInfo fields

Field Type Description
serviceTypeName String! Friendly name of the service interface (e.g. IServiceTrain<OrderInput, OrderResult>)
implementationTypeName String! Friendly name of the concrete class
inputTypeName String! Friendly name of the input type
outputTypeName String! Friendly name of the output type
lifetime String! DI lifetime (Singleton, Scoped, Transient)
inputSchema [InputPropertySchema!]! Public readable properties on the input type

InputPropertySchema fields

Field Type Description
name String! Property name
typeName String! Friendly type name (e.g. String, Int32, DateTime?)
isNullable Boolean! Whether the property is nullable

manifests

Returns a paginated list of scheduler manifests, ordered by ID descending (newest first). Supports both offset-based and keyset cursor pagination.

query {
  operations {
    manifests(skip: 0, take: 10) {
      items {
        id
        externalId
        name
        isEnabled
        scheduleType
        cronExpression
        intervalSeconds
        maxRetries
        timeoutSeconds
        lastSuccessfulRun
        manifestGroupId
        dependsOnManifestId
        priority
      }
      totalCount
      isEstimatedCount
      skip
      take
      nextCursor
    }
  }
}
Parameter Type Default Description
skip Int 0 Number of records to skip (offset pagination)
take Int 25 Number of records to return
afterId Long null Keyset cursor. Returns records with id < afterId. When provided, skip is ignored. See Pagination

Returns: PagedResult<ManifestSummary>

ManifestSummary fields

Field Type Description
id Long! Database ID
externalId String! Unique external identifier (used for upsert/trigger)
name String! Train type name
isEnabled Boolean! Whether the manifest is active
scheduleType ScheduleType! Cron or Interval
cronExpression String Cron expression (when scheduleType is Cron)
intervalSeconds Int Interval in seconds (when scheduleType is Interval)
maxRetries Int! Maximum retry count on failure
timeoutSeconds Int Execution timeout
lastSuccessfulRun DateTime Timestamp of last successful execution
manifestGroupId Long! Parent group ID
dependsOnManifestId Long ID of the manifest this one depends on
priority Int! Dispatch priority (0-31, higher runs first)

manifest

Returns a single manifest by database ID.

query {
  operations {
    manifest(id: 42) {
      id
      externalId
      name
      isEnabled
      scheduleType
      cronExpression
      priority
    }
  }
}
Parameter Type Required Description
id Long! Yes The manifest’s database ID

Returns: ManifestSummary (nullable, returns null if the ID does not exist)


manifestGroups

Returns a paginated list of manifest groups, ordered by ID descending. Supports both offset-based and keyset cursor pagination.

query {
  operations {
    manifestGroups(skip: 0, take: 10) {
      items {
        id
        name
        maxActiveJobs
        priority
        isEnabled
        createdAt
        updatedAt
      }
      totalCount
      isEstimatedCount
      skip
      take
      nextCursor
    }
  }
}
Parameter Type Default Description
skip Int 0 Number of records to skip (offset pagination)
take Int 25 Number of records to return
afterId Long null Keyset cursor. Returns records with id < afterId. See Pagination

Returns: PagedResult<ManifestGroupSummary>

ManifestGroupSummary fields

Field Type Description
id Long! Database ID
name String! Group name
maxActiveJobs Int Concurrency limit for the group (null = unlimited)
priority Int! Default priority for manifests in this group
isEnabled Boolean! Whether the group is active
createdAt DateTime! When the group was created
updatedAt DateTime! When the group was last modified

executions

Returns a paginated list of train executions (metadata records), ordered by ID descending (newest first). Supports both offset-based and keyset cursor pagination.

query {
  operations {
    executions(skip: 0, take: 10) {
      items {
        id
        externalId
        name
        trainState
        startTime
        endTime
        failureJunction
        failureReason
        manifestId
        cancellationRequested
      }
      totalCount
      isEstimatedCount
      skip
      take
      nextCursor
    }
  }
}
Parameter Type Default Description
skip Int 0 Number of records to skip (offset pagination)
take Int 25 Number of records to return
afterId Long null Keyset cursor. Returns records with id < afterId. See Pagination

Returns: PagedResult<ExecutionSummary>

ExecutionSummary fields

Field Type Description
id Long! Metadata ID
externalId String! External identifier
name String! Train type name
trainState TrainState! Current state (Pending, InProgress, Completed, Failed, Cancelled)
startTime DateTime! When execution began
endTime DateTime When execution finished (null if still running)
failureJunction String Name of the junction that failed (null if no failure)
failureReason String Exception message on failure
manifestId Long Associated manifest ID (null if not scheduler-initiated)
cancellationRequested Boolean! Whether cancellation was requested

execution

Returns a single execution by metadata ID.

query {
  operations {
    execution(id: 100) {
      id
      externalId
      name
      trainState
      startTime
      endTime
      failureJunction
      failureReason
    }
  }
}
Parameter Type Required Description
id Long! Yes The execution’s metadata ID

Returns: ExecutionSummary (nullable, returns null if the ID does not exist)


PagedResult

All paginated queries return the same wrapper type:

Field Type Description
items [T!]! The page of results
totalCount Int! Total number of records matching the query
skip Int! The skip value that was applied
take Int! The take value that was applied
isEstimatedCount Boolean! true when totalCount is a fast estimate rather than an exact count. See Pagination
nextCursor Long ID of the last item in the page. Pass as afterId to fetch the next page via keyset pagination. null when no items are returned

Pagination

Paginated queries support two strategies. Both can be used interchangeably. The dashboard uses offset pagination internally, while API consumers can opt into keyset cursors for better deep-page performance.

Offset pagination (default)

Pass skip and take as before. This uses SQL OFFSET/LIMIT under the hood. Performance degrades on deep pages (high skip values) because the database must scan and discard rows up to the offset.

query {
  operations {
    executions(skip: 100, take: 25) { items { id } totalCount }
  }
}

Keyset cursor pagination

Pass afterId (the nextCursor from the previous page) instead of skip. This uses WHERE id < @afterId, which is constant-time regardless of how deep you paginate because it seeks directly to the cursor position via the primary key index.

# First page
query {
  operations {
    executions(take: 25) { items { id } totalCount nextCursor }
  }
}

# Next page: pass nextCursor as afterId
query {
  operations {
    executions(afterId: 4201, take: 25) { items { id } totalCount nextCursor }
  }
}

When afterId is provided, skip is ignored.

Estimated counts

For unfiltered queries on large tables (>10,000 rows), totalCount uses PostgreSQL’s pg_class.reltuples statistic instead of an exact COUNT(*). This is O(1) rather than O(n), and the difference matters when the metadata table has millions of rows.

When the estimate is used, isEstimatedCount is true. The estimate is updated by PostgreSQL’s autovacuum/autoanalyze and is typically accurate within a few percent. For filtered queries or small tables, an exact count is always used and isEstimatedCount is false.


Back to top

Trax - A .NET framework for Railway Oriented Programming with Effects, Scheduling, and more