IPersistedOperationValidator
Validates a candidate GraphQL document before IPersistedOperationStore.UpsertAsync writes a row. Runs at upload time so operators see schema mismatches immediately rather than at request time when a client tries to run the operation.
Interface
public interface IPersistedOperationValidator
{
Task ValidateAsync(string document, CancellationToken ct);
}Implementations throw one of the PersistedOperationException subclasses on failure. The storage layer lets these propagate; no row is written and no broadcast fires when validation throws.
Implementations
| Implementation | Registered by | Behavior |
|---|---|---|
HotChocolateSchemaValidator | UsePersistedOperations(...) on TraxGraphQLBuilder | Parses with Utf8GraphQLParser, then runs HotChocolate's IDocumentValidator against the live schema. Throws PersistedOperationParseException for syntax errors and PersistedOperationValidationException for schema mismatches (unknown field, wrong variable type, missing required variable, etc.). |
NoOpPersistedOperationValidator | AddPersistedOperationStore(...) (standalone admin path) | Accepts any input. Use this in hosts that do not have a HotChocolate schema in process (CI manifest uploaders, one-off scripts). The shape-diff guardrail still runs at the storage layer. |
The HotChocolate-backed implementation resolves IRequestExecutorResolver lazily on first call so the validator can be constructed before GraphQL composition is final. The resolved executor and the schema-specific IDocumentValidator are cached for the lifetime of the validator.
Example
The validator usually fires implicitly inside UpsertAsync, but it can be resolved directly when admin tooling wants to validate before posting a payload:
var validator = serviceProvider.GetRequiredService<IPersistedOperationValidator>();
try
{
await validator.ValidateAsync(candidateDocument, ct);
}
catch (PersistedOperationValidationException ex)
{
foreach (var failure in ex.Failures)
Console.Error.WriteLine($"{failure.Message} (line {failure.Locations.FirstOrDefault().Line})");
return;
}