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
| Service | Lifetime | Purpose |
|---|---|---|
PersistedOperationsOptions | Singleton | Resolved configuration. |
IPersistedOperationCache -> NoOp or InMemory | Singleton | Cache layer. No-op unless WithInMemoryCache() was called. |
IPersistedOperationBroadcaster -> NoOp or RabbitMq | Singleton | Multi-node invalidation. RabbitMQ with UseRabbitMqInvalidation(), no-op with SingleNode(). |
PersistedOperationReceiverService | Hosted (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 -> DbPersistedOperationStorage | Singleton | Programmatic 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. |
IPersistedOperationsService | Singleton (TryAdd) | The management surface the GraphQL fields and the dashboard call. |
IOperationDocumentStorage -> DbPersistedOperationStorage | Singleton | HotChocolate hot-path lookup. |
IPersistedOperationValidator -> HotChocolateSchemaValidator | Singleton (Replace) | Runs HotChocolate validation against the live schema before every upsert. Overrides the no-op default from AddPersistedOperationStore. |
IPersistedOperationsCapability | Singleton | Marker 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. |
AllowlistMatcher | Singleton | Used by the enforcement middleware. |
PersistedOperationPolicy | Singleton | The enforcement decision, asked once per operation by the request middleware UsePersistedOperations adds after HotChocolate's document parser. |
TimeProvider | Singleton (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:
| Misconfig | Behavior |
|---|---|
UseDatabase not called | Throws: "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 twice | Throws. |
AllowOperations contains an empty entry | Throws. |