ValidateGraphQLClientAssembliesAsync

Validates every request type a client owns in a set of assemblies against that client's schema, so a query the server no longer accepts fails at startup or in a test instead of on first use. Call it after app.Build().

Signatures

public static Task ValidateGraphQLClientAssembliesAsync(
    this IServiceProvider services,
    params Assembly[] assemblies)
 
public static Task ValidateGraphQLClientAssembliesAsync(
    this IServiceProvider services,
    object serviceKey,
    params Assembly[] assemblies)
ParameterTypeRequiredDescription
servicesIServiceProviderYesThe built container, usually app.Services
serviceKeyobjectKeyed overloadThe key the client was registered with by AddKeyedTraxGraphQLClient
assembliesAssembly[]YesThe assemblies to scan

Returns: a Task that completes when every request has validated.

Which requests are checked

Every concrete class in assemblies that implements IGenericGraphQLClientRequest and belongs to the client:

  • The unkeyed overload checks the types that carry no [GraphQLClient] mark, against the unkeyed client's schema.
  • The keyed overload checks the types marked [GraphQLClient(serviceKey)], against the schema of the client registered under that key.

Each type is created with RuntimeHelpers.GetUninitializedObject, without running a constructor, and its Query is validated. A Query must therefore not depend on state a constructor sets. A literal string, an embedded resource (GraphQLResourceRequest) and a TypedRequest all qualify. Validation stops at the first failure.

Exceptions

ExceptionWhen
GraphQLValidationExceptionA query does not validate against the schema, has no operation, or has more than one. Query holds the failing text and Errors the validator's errors
GraphQLSchemaIntrospectionExceptionThe schema could not be loaded (introspection failed, the SDL file is missing or invalid)
InvalidOperationExceptionNo request type in assemblies belongs to this client, or (keyed overload) no client is registered under serviceKey

Finding nothing to validate is refused on purpose: it almost always means a request was left unmarked or marked with a misspelt key, and passing would hide that.

Example

var app = builder.Build();
 
await app.Services.ValidateGraphQLClientAssembliesAsync(typeof(Program).Assembly);
await app.Services.ValidateGraphQLClientAssembliesAsync("billing", typeof(Program).Assembly);
 
app.Run();

In a test, the same call over a container built with UseFileSchema checks every query against the checked-in schema with no server running.

Validating with a custom filter

IGraphQLClientValidator, which every client registers, carries an extension for scanning without the ownership rule:

public static Task ValidateAssembliesAsync(
    this IGraphQLClientValidator validator,
    IEnumerable<Assembly> assemblies,
    CancellationToken cancellationToken = default)
 
public static Task ValidateAssembliesAsync(
    this IGraphQLClientValidator validator,
    IEnumerable<Assembly> assemblies,
    Func<Type, bool>? typeFilter,
    CancellationToken cancellationToken = default)

It validates every request type typeFilter accepts (all of them when null), whatever its [GraphQLClient] mark, and does not fail when it finds none.

Remarks

  • For host-startup gating, UseStartupValidation on the builder does the same check from a hosted service, so the host refuses to start on a failure.
  • The validator caches each query text that passes, so validating at startup also warms the cache the executor uses.

Package

dotnet add package Trax.Api.GraphQL.Client