GraphQL Setup¶
Verified by tests
ServiceRegistrationTests, WhizbangGraphQLOptionsTests, ScopeMiddlewareExtensionsTests — library CI run #31657041675 (2026-08-13)
This guide covers installation and configuration of Whizbang's HotChocolate GraphQL integration.
Installation¶
Installation
Basic Configuration¶
Minimal Setup¶
Minimal Setup
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddGraphQLServer()
.AddWhizbangLenses()
.AddQueryType<Query>();
var app = builder.Build();
app.MapGraphQL();
app.Run();
With Scope Middleware¶
For multi-tenancy and security filtering:
With Scope Middleware
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddWhizbangScope();
builder.Services
.AddGraphQLServer()
.AddWhizbangLenses()
.AddQueryType<Query>();
var app = builder.Build();
// Middleware order matters
app.UseAuthentication();
app.UseWhizbangScope(); // After auth, before GraphQL
app.MapGraphQL();
app.Run();
Configuration Options¶
WhizbangGraphQLOptions¶
Configure default behavior for all lenses:
WhizbangGraphQLOptions
builder.Services
.AddGraphQLServer()
.AddWhizbangLenses(options => {
options.DefaultScope = GraphQLLensScopes.Data | GraphQLLensScopes.SystemFields;
options.DefaultPageSize = 25;
options.MaxPageSize = 200;
});
| Option | Default | Description |
|---|---|---|
DefaultScope |
GraphQLLensScopes.DataOnly |
Fields exposed when a lens does not set an explicit scope |
DefaultPageSize |
10 |
Default page size for paging |
MaxPageSize |
100 |
Maximum allowed page size |
IncludeMetadataInFilters |
true |
Include metadata fields in filter/sort types (when scope includes Metadata) |
IncludeScopeInFilters |
true |
Include scope fields in filter/sort types (when scope includes Scope) |
WhizbangScopeOptions¶
Configure scope extraction from HTTP context:
WhizbangScopeOptions
builder.Services.AddWhizbangScope(options => {
// Claim types
options.TenantIdClaimType = "tenant_id";
options.UserIdClaimType = ClaimTypes.NameIdentifier;
options.OrganizationIdClaimType = "org_id";
options.CustomerIdClaimType = "customer_id";
// Header names (fallback if claim not present)
options.TenantIdHeaderName = "X-Tenant-Id";
options.UserIdHeaderName = "X-User-Id";
// Custom extensions
options.ExtensionClaimMappings["region"] = "Region";
options.ExtensionHeaderMappings["X-Region"] = "Region";
});
| Option | Default | Description |
|---|---|---|
TenantIdClaimTypes |
["tenant_id"] |
JWT claim types for tenant ID, tried in order |
TenantIdHeaderName |
"X-Tenant-Id" |
HTTP header for tenant ID |
UserIdClaimTypes |
Azure AD objectidentifier, objectid, oid, sub, ClaimTypes.NameIdentifier |
JWT claim types for user ID, tried in order |
RolesClaimType |
ClaimTypes.Role |
JWT claim for roles |
GroupsClaimTypes |
["groups"] |
JWT claim types for group memberships |
PermissionsClaimTypes |
["permissions"] |
JWT claim types for permissions |
CorrelationIdHeaderName |
"X-Correlation-ID" |
Header whose Guid value is adopted as the inbound correlation ID |
Each *ClaimType (singular) property still exists as a backwards-compatible shim: getting it reads the first entry of the corresponding *ClaimTypes list, and setting it replaces the list with a single value. Multi-valued claims (permissions, groups) honor a ClaimAggregation strategy (PermissionsAggregation/GroupsAggregation, default FirstMatch).
Query Type Setup¶
Define your query type with lens resolvers:
Query Type Setup
public class Query {
[UsePaging(DefaultPageSize = 10, MaxPageSize = 100, IncludeTotalCount = true)]
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<PerspectiveRow<OrderReadModel>> GetOrders(
[Service] IOrderLens lens) {
return lens.Query;
}
[UsePaging]
[UseFiltering]
[UseSorting]
public IQueryable<PerspectiveRow<ProductReadModel>> GetProducts(
[Service] IProductLens lens) {
return lens.Query;
}
}
Updated
ILensQuery<TModel>.Query is the legacy accessor and is marked [Obsolete] at this commit in favor of the fluent scope API (lens.DefaultScope.Query or lens.Scope(QueryScope.X).Query). It still works — the generated lens resolvers currently use lens.Query directly — but expect an obsolete-usage warning in hand-written resolvers.
Service Registration¶
Register your lens implementations:
Service Registration
// If using EF Core
builder.Services.AddScoped<IOrderLens, EFCoreOrderLens>();
builder.Services.AddScoped<IProductLens, EFCoreProductLens>();
// Register the generated lens query fields (extension on the GraphQL builder,
// generated by the source generator - call after AddWhizbangLenses())
builder.Services
.AddGraphQLServer()
.AddWhizbangLenses()
.AddWhizbangLensQueries();
What Gets Registered¶
AddWhizbangLenses() registers:
WhizbangFilterConvention- Custom filtering forPerspectiveRow<T>WhizbangSortConvention- Custom sorting for nested data- Default projection convention
WhizbangGraphQLOptionssingleton
AddWhizbangScope() registers:
IScopeContextAccessor- AsyncLocal-based scope accessWhizbangScopeOptionssingleton (if configured)
Next Steps¶
- Lens Integration - Configure
[GraphQLLens]attributes - Filtering - Query filtering examples
- Scoping - Multi-tenancy configuration