UsePersistedOperations

Fluent extension on TraxGraphQLBuilder that wires the persisted-operation pipeline: storage, optional cache, optional cross-node invalidation, allowlist, introspection bypass, and shadow-mode logging.

Signature

public static TraxGraphQLBuilder UsePersistedOperations(
    this TraxGraphQLBuilder builder,
    Action<PersistedOperationsBuilder> configure
);

Usage

One node:

services.AddTraxGraphQL(graphql => graphql
    .UsePersistedOperations(opts => opts
        .UseDatabase(connectionString)
        .RequirePersisted(true)
        .SingleNode()
    )
);

More than one node, with the optional Trax lookup cache:

services.AddTraxGraphQL(graphql => graphql
    .UsePersistedOperations(opts => opts
        .UseDatabase(connectionString)
        .RequirePersisted(true)
        .WithInMemoryCache(c => c.WithTtl(TimeSpan.FromMinutes(15)))
        .UseRabbitMqInvalidation(rabbitConnectionString)
    )
);

One of SingleNode() and UseRabbitMqInvalidation(...) is required; see One node or many.

See PersistedOperationsBuilder for every method.

What gets registered

ServiceLifetimePurpose
PersistedOperationsOptionsSingletonResolved configuration.
IPersistedOperationCache -> NoOp or InMemorySingletonCache layer. No-op unless WithInMemoryCache() was called.
IPersistedOperationBroadcaster -> NoOp or RabbitMqSingletonMulti-node invalidation. RabbitMQ with UseRabbitMqInvalidation(), no-op with SingleNode().
PersistedOperationReceiverServiceHosted (when broadcaster is RabbitMQ)Subscribes to the fanout exchange and empties HotChocolate's caches and the Trax cache on broadcast, on losing the broker connection, and on recovering it.
IPersistedOperationStore -> DbPersistedOperationStorageSingletonProgrammatic CRUD. Reads and writes through the Effect data provider's IDataContextProviderFactory, so the host needs UsePostgres (or another data provider); the tables are sets on the Effect DataContext.
IPersistedOperationsServiceSingleton (TryAdd)The management surface the GraphQL fields and the dashboard call.
IOperationDocumentStorage -> DbPersistedOperationStorageSingletonHotChocolate hot-path lookup.
IPersistedOperationValidator -> HotChocolateSchemaValidatorSingleton (Replace)Runs HotChocolate validation against the live schema before every upsert. Overrides the no-op default from AddPersistedOperationStore.
IPersistedOperationsCapabilitySingletonMarker meaning this process serves the management GraphQL fields. The dashboard gates its pages on IPersistedOperationsService instead, so they also appear on a host that registers only AddPersistedOperationStore.
AllowlistMatcherSingletonUsed by the enforcement middleware.
PersistedOperationPolicySingletonThe enforcement decision, asked once per operation by the request middleware UsePersistedOperations adds after HotChocolate's document parser.
TimeProviderSingleton (TryAdd)Default TimeProvider.System; override for tests.

Also extends the GraphQL schema with the management mutations and queries, and calls ExposeOperationQueries() / ExposeOperationMutations() on the builder so RootQuery and RootMutation are emitted even when the host has not registered any train-backed queries or mutations.

Because this exposes the operations namespace (including the persisted-operation management mutations, which are admin operations), the host must answer for it, or AddTraxGraphQL() fails at startup: GateOperations(policy, roles) to gate the namespace while the rest of the endpoint stays open, RequireAuthorization() to gate the whole endpoint, or AllowAnonymousOperations() to opt into anonymous access.

Declining the namespace

Persisted operations and a GraphQL-exposed scheduler console are separable. ExposeOperationsNamespace(false) wires storage, enforcement, the cache and cross-node invalidation without touching the schema, for a host that manages its operations out of band (a migration, a deploy step, a separate admin process):

.UsePersistedOperations(po => po
    .UseDatabase(connectionString)
    .RequirePersisted(true)
    .SingleNode()
    .ExposeOperationsNamespace(false)
)

With the namespace declined there is nothing to gate and nothing to acknowledge. The management mutations are not in the schema either: they are [ExtendObjectType(typeof(OperationsMutations))] classes, and their target type is not there to extend.

Validation

Configuration errors throw InvalidOperationException at startup. Each message names the misconfigured method and suggests a fix:

MisconfigBehavior
UseDatabase not calledThrows: "UseDatabase(connectionString) is required."
RequirePersisted(false) and LogNonPersistedRequests(false)Throws: configuration does nothing.
Neither SingleNode() nor UseRabbitMqInvalidation(...)Throws: names both, since HotChocolate's caches do not expire and a change reaches another node only by broadcast.
Both SingleNode() and UseRabbitMqInvalidation(...)Throws: the two contradict each other.
WithInMemoryCache called twiceThrows.
AllowOperations contains an empty entryThrows.