Skip to content

Upgrade Hot Chocolate to version 16.0.0#3480

Merged
aaronburtle merged 32 commits into
mainfrom
dev/aaronburtle/HC-16-upgrade
May 16, 2026
Merged

Upgrade Hot Chocolate to version 16.0.0#3480
aaronburtle merged 32 commits into
mainfrom
dev/aaronburtle/HC-16-upgrade

Conversation

@aaronburtle
Copy link
Copy Markdown
Contributor

@aaronburtle aaronburtle commented Apr 29, 2026

Why make this change?

Closes #3448

Hot Chocolate has released stable v16.0.0 and we upgrade DAB to that version.

What is this change?

Update deps

Bump 6 HC packages to 16.0.0 in Directory.Packages.props:
HotChocolate, HotChocolate.AspNetCore, HotChocolate.AspNetCore.Authorization,
HotChocolate.Types.NodaTime, HotChocolate.Utilities.Introspection,
HotChocolate.Diagnostics.

Breaking changes (GraphQL schema)

HC v16 forces / motivates two schema-type renames. Wire formats and runtime
values are unchanged for both. REST and CLI surface area is unchanged.

Scalar: Byte -> UnsignedByte
Filter input: ByteFilterInput -> UnsignedByteFilterInput

HC v16 split the legacy ByteType into a signed ByteType (sbyte,
-128..127) and a new UnsignedByteType (byte, 0..255). SQL Server tinyint
is unsigned, so DAB must bind to UnsignedByteType.

Scalar: ByteArray -> Base64String
Filter input: ByteArrayFilterInput -> Base64StringFilterInput

HC v16 marked ByteArrayType as [Obsolete] in favor of Base64StringType.
Both serialize byte[] as a base64-encoded JSON string (identical wire
format), but the GraphQL scalar name is now Base64String. DAB targets the
new name so the generated schema does not depend on a deprecated scalar.

Inline literal filters continue to work for both renames. Clients that
declare typed variables of ByteFilterInput or ByteArrayFilterInput, or
that regenerate code from the schema (graphql-codegen, Strawberry Shake,
etc.), must update the type names.

Refactor scalar APIs

v16 renamed scalar overrides (ParseLiteral/ParseValue ->
OnCoerceInputLiteral/OnCoerceInputValue/OnCoerceOutputValue/
OnValueToLiteral) and call-site helpers (ParseValue/ParseResult ->
ValueToLiteral). DateTimeType.ValueToLiteral only accepts DateTimeOffset,
so we convert at the boundary. HC v16 also elides trailing zero fractional
seconds on DateTime output (...:54.000Z -> ...:54Z), which surfaced in
several test assertion updates.

Refactor resolver/execution APIs

ISelection was removed (use concrete Selection); Selection.SyntaxNode
became a ReadOnlySpan<FieldSelectionNode> (SyntaxNodes) to support
field-merging; TimeSpanType was replaced by DurationType (ISO-8601,
parsed via XmlConvert.ToTimeSpan); OperationResult.WithContextData is
gone (set singleResult.ContextData directly).

Refactor request interceptor and status middleware

IntrospectionInterceptor now has a no-arg constructor (schema services are
isolated from request services in v16) and implements the new four-arg
OnCreateAsync(HttpContext, IRequestExecutor, OperationRequestBuilder, CancellationToken)
signature; RuntimeConfigProvider is resolved from
context.RequestServices at request time.

DetermineStatusCodeMiddleware was updated for the unified OperationResult
type. Status codes are now propagated via context-data
(ExecutionContextData.HttpStatusCode) using an ImmutableDictionary
builder rather than mutating the response directly.

Adopt lazy schema initialization (LazyInitialization = true)

HC v16 builds the schema eagerly during host startup by default. DAB
supports a "hosted" scenario where the runtime config is supplied after the
host starts (POST /configuration), so eager construction races with our
placeholder fallback. We opt back into the v15 default
(options.LazyInitialization = true) so the schema is built on the first
GraphQL request, by which time the runtime config and metadata provider are
ready.

Drop ModifyOptions(o => o.EnableOneOf = true)

OneOf is on by default in v16.

Refactor startup

DateTimeType(disableFormatCheck: bool) is obsolete (replaced by
DateTimeOptions { ValidateInputFormat = ... }, polarity flipped);
WithOptions only accepts Action<GraphQLServerOptions> (per-request);
MapNitroApp().WithOptions(GraphQLToolOptions) was removed. Nitro is now
served by the same /graphql endpoint and is governed by
options.Tool.Enable on the per-request server options.

Harden Selection.SyntaxNodes access

HC v16's Selection.SyntaxNodes is a ReadOnlySpan<FieldSelectionNode>,
so direct SyntaxNodes[0].Node indexing would surface as
IndexOutOfRangeException at request time if the span were ever empty.
Added Selection.RequireFieldNode() in SelectionExtensions.cs
(IsEmpty check, return SyntaxNodes[0].Node, throw targeted
DataApiBuilderException otherwise). Adopted at all call sites in
SqlQueryStructure, CosmosQueryStructure, and ExecutionHelper.

Test updates

Adopt v16 OperationResultData / ResultDocument / ResultElement /
ResultProperty traversal in MultiSourceQueryExecutionUnitTests. Update
TestNoConfigReturnsServiceUnavailable to recognize that lazy
WithOptions resolution surfaces "no runtime config" as
DataApiBuilderException(ServiceUnavailable) rather than a 503 response or
ApplicationException. Update DateTime literal assertions and filter input
name references in affected test files. Decouple the GraphQL-scalar name
from the test database column name in GraphQLSupportedTypesTestsBase and
the My/Pg test classes so the Byte/UnsignedByte and
ByteArray/Base64String renames do not require renaming
byte_types/bytearray_types columns.

How was this tested?

Run against current test suite.

Bumps the HotChocolate family of packages from 16.0.0-p.7.68 (preview)
to 16.0.0-rc.1.43 (release candidate). Affected packages:
  - HotChocolate
  - HotChocolate.AspNetCore
  - HotChocolate.AspNetCore.Authorization
  - HotChocolate.ModelContextProtocol
  - HotChocolate.Types.NodaTime
  - HotChocolate.Utilities.Introspection
  - HotChocolate.Transport.Http
  - HotChocolate.Diagnostics
  - CookieCrumble

This commit is intentionally a single-line version bump; the API surface
changes required to compile against rc.1.43 are split across the
following commits in this series.

Note: rc.1.43 is not yet cached on the internal feed
(pkgs.dev.azure.com/sqldab/data_api_builder_build_packages). The
NuGetAuthenticate@1 step in the build pipeline will trigger an
authenticated upstream fetch on the first restore, after which the
package will be permanently cached for all subsequent builds.
Hot Chocolate v16 reshaped the scalar type API surface. This commit
adopts the new pattern in DAB's custom and built-in scalar usages.

Scalar override renames (Service.GraphQLBuilder/CustomScalars/SingleType.cs):
  ParseLiteral(IFloatValueLiteral)  -> OnCoerceInputLiteral(IFloatValueLiteral)
  ParseValue(JsonElement)           -> OnCoerceInputValue(JsonElement)
  ParseResult / FormatResult        -> OnCoerceOutputValue(T, ResultElement)
  ParseValue(T runtime)             -> OnValueToLiteral(T)

The output methods now write into a HotChocolate.Text.Json.ResultElement
buffer rather than returning a value, and OnValueToLiteral replaces the
old "ValueToLiteral / ParseResult / ParseValue" trio with a single
literal-producing override.

Scalar invocation renames at call sites
(GraphQLStoredProcedureBuilder.cs, Sql/SchemaConverter.cs):
  type.ParseValue(runtimeValue)  -> type.ValueToLiteral(runtimeValue)
  type.ParseResult(runtimeValue) -> type.ValueToLiteral(runtimeValue)

Type rename (GraphQLTypes/DefaultValueType.cs, GraphQLStoredProcedureBuilder.cs,
Sql/SchemaConverter.cs):
  ByteArrayType -> Base64StringType
The legacy ByteArrayType identifier is now [Obsolete] and the
Base64-string-encoded byte-array scalar lives under Base64StringType.

DateTime / DateTimeOffset boundary
(GraphQLStoredProcedureBuilder.cs, Sql/SchemaConverter.cs):
HC v16's DateTimeType.ValueToLiteral(...) accepts only DateTimeOffset.
Inputs from config (System.DateTime) are converted at the boundary
preserving DateTimeKind:
  - Unspecified -> DateTimeOffset(value, TimeSpan.Zero)
  - Local / Utc -> DateTimeOffset(value)
The CLR-driven schema-converter path now parses datetime defaults as
DateTimeOffset directly rather than via DateTime.

These changes match the patterns proposed in PR #3136 (HC v16 milestone-11
migration) and continue to compile cleanly against rc.1.43.
…tData

Migrates DAB's Hot Chocolate runtime integration to v16's resolver and
execution-pipeline contracts.

Selection -> concrete type (Resolvers/CosmosQueryStructure.cs):
  ISelection -> Selection
The ISelection interface was removed in HC v16; the runtime always
returns the concrete Selection type.

Selection.SyntaxNode -> SyntaxNodes[0].Node
(Resolvers/CosmosQueryStructure.cs,
 Resolvers/Sql Query Structures/SqlQueryStructure.cs,
 Services/ExecutionHelper.cs)

HC v16 introduces field-merging during query analysis, so a single
Selection can now be backed by multiple syntactic occurrences in the
query document. The shape changed from a single FieldNode (SyntaxNode)
to an immutable list (SyntaxNodes) whose first entry is the canonical
node for code that does not yet handle merged fields. DAB does not
currently exercise merging, so .SyntaxNodes[0].Node is the equivalent
canonical node and matches PR #3136's pattern.

TimeSpanType -> DurationType (Services/ExecutionHelper.cs):
The TimeSpanType scalar was removed; its replacement DurationType
serializes values as ISO-8601 duration strings (e.g. "PT1H30M") rather
than .NET's "1.06:00:00" round-trip format. The runtime-coercion path
now parses with System.Xml.XmlConvert.ToTimeSpan, which is the
ISO-8601-aware parser shipped in BCL.

ByteArrayType -> Base64StringType (Services/ExecutionHelper.cs):
Same scalar rename as the GraphQLBuilder commit; applied here in the
runtime fieldValue coercion switch.

OperationResult.WithContextData(...) -> direct property setter
(Services/DetermineStatusCodeMiddleware.cs):
  context.Result = singleResult.WithContextData(contextData.ToImmutable());
        is now
  singleResult.ContextData = contextData.ToImmutable();
OperationResult is mutable in v16, so the WithContextData fluent
helper was removed; mutate ContextData directly. The middleware no
longer needs to reassign context.Result since the existing instance
reference now carries the change.
The OneOf input-object directive (graphql/graphql-spec#825) is
permanently enabled in Hot Chocolate v16 and the SchemaOptions.EnableOneOf
toggle was removed. Drops the now-obsolete .ModifyOptions(o => o.EnableOneOf = true)
configuration call from the schema builder; behavior is unchanged.
…delegate

Updates the GraphQL server wire-up in Startup.cs for three v16 API
changes:

1. DateTimeType options bag
   The bool-flag overload `new DateTimeType(disableFormatCheck: bool)`
   is now [Obsolete]. Replaced with `new DateTimeType(new DateTimeOptions
   { ValidateInputFormat = ... })`. Note the boolean polarity flipped:
     old: disableFormatCheck = !graphQLRuntimeOptions.EnableLegacyDateTimeScalar
     new: ValidateInputFormat = !graphQLRuntimeOptions.EnableLegacyDateTimeScalar
   ...so the resulting condition that controls validation is unchanged.

2. WithOptions accepts only Action<GraphQLServerOptions>
   The MapGraphQL().WithOptions(...) overload that took a literal
   GraphQLServerOptions instance was removed. Options are now configured
   per-request via a delegate:
     .WithOptions(options => options.Tool.Enable = IsUIEnabled(...));
   This means options are evaluated lazily on each incoming request
   rather than captured eagerly at startup. The behavioral difference
   is exercised by ConfigurationTests.TestNoConfigReturnsServiceUnavailable;
   see the test commit later in this series.

3. Nitro mapping consolidated under GraphQL server options
   `endpoints.MapNitroApp().WithOptions(new GraphQLToolOptions { ... })`
   was removed; GraphQLToolOptions is no longer a public type. The
   Nitro / Banana Cake Pop IDE is now governed solely by
   GraphQLServerOptions.Tool.Enable on the GraphQL endpoint mapping.
   Dropped the explicit MapNitroApp block because Tool.Enable already
   covers the dev-mode disable case.
Updates two test files affected by Hot Chocolate v16 runtime API
changes that altered observable behavior.

MultiSourceQueryExecutionUnitTests.cs - OperationResult.Data shape:
HC v16 changed `OperationResult.Data` from
`IReadOnlyDictionary<string, object>` to
`OperationResultData?` (a struct wrapping a `ResultDocument`). The
`Errors` property is also no longer nullable; it is an immutable list
with an `IsEmpty` member.

  Old:
    Assert.IsNull(singleResult.Errors, ...);
    IReadOnlyDictionary<string, object> data = singleResult.Data;
    data.TryGetValue(QUERY_NAME_1, out object queryNode1);
    var firstEntry = ((IReadOnlyDictionary<string, object>)queryNode1)
                         .FirstOrDefault();
    Assert.AreEqual("db1", firstEntry.Value, ...);

  New:
    Assert.IsTrue(singleResult.Errors.IsEmpty, ...);
    ResultDocument document = (ResultDocument)singleResult.Data.Value.Value;
    document.Data.TryGetProperty(QUERY_NAME_1, out ResultElement queryNode1);
    ResultProperty firstEntry = queryNode1.EnumerateObject().FirstOrDefault();
    Assert.AreEqual("db1", firstEntry.Value.GetString(), ...);

The traversal is now: OperationResultData (struct) -> .Value (object,
the ResultDocument) -> .Data (root ResultElement) -> .TryGetProperty
-> .EnumerateObject -> ResultProperty -> .Value.GetString().

ConfigurationTests.cs - exception contract for "no runtime config":
HC v16 resolves WithOptions(Action<>) per request rather than eagerly
at startup, so the "no runtime config" condition that previously
manifested as a 503 response (hosted-on-demand scenario) or
ApplicationException (CLI startup scenario) now bubbles out of the
request pipeline as DataApiBuilderException(HttpStatusCode.ServiceUnavailable).

The catch block in TestNoConfigReturnsServiceUnavailable is split into
two typed handlers so the assertion remains precise:

  catch (DataApiBuilderException dabException)
      Assert StatusCode == ServiceUnavailable.
      (Covers all three [DataRow] cases under HC v16.)

  catch (ApplicationException appException)
      Assert isUpdateableRuntimeConfig == false.
      Assert message matches the historical CLI startup message.
      (Preserved for symmetry; not exercised under HC v16 runtime
       behavior on the in-process test host but kept to avoid silently
       weakening the contract for legacy hosting variants.)

Added a remarks block on the test summarizing the behavioral change.
Adds `#nullable enable annotations` to three test files that contain
nullable reference type annotations (e.g. `string?`) but live in the
Service.Tests project, which has `<Nullable>disable</Nullable>` set
project-wide.

Without an explicit annotation context the compiler emits CS8632
("The annotation for nullable reference types should only be used in
code within a '#nullable' annotations context"), which the repo-wide
`<TreatWarningsAsErrors>True</TreatWarningsAsErrors>` setting promotes
to a build error.

These errors were introduced on main by an earlier change (PR #3476)
and surfaced here only because the HC 16 upgrade pulled the build all
the way through Service.Tests. The fix is intentionally narrow:
"#nullable enable annotations" allows `?` syntax without enabling
flow-sensitive null-state checking, so it does not cascade into a
sea of unrelated nullable-state diagnostics in these legacy test
files.

Files affected:
  - Service.Tests/UnitTests/RequestParserUnitTests.cs
  - Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs
  - Service.Tests/Configuration/RuntimeConfigLoaderTests.cs

This commit is independent of the HC 16 upgrade and could land
separately on main.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates DAB’s GraphQL stack to align with Hot Chocolate v16 RC APIs, unblocking progress toward the finalized v16 surface area (per #3448) while keeping existing DAB REST/GraphQL/CLI behavior consistent.

Changes:

  • Bump Hot Chocolate (and related) package versions from 16.0.0-p.7.68 to 16.0.0-rc.1.43.
  • Migrate to v16 API changes across scalars, selection/syntax access, duration handling, and server endpoint options.
  • Update unit tests for new Hot Chocolate result/data traversal and address nullable-annotation build errors under warnings-as-errors.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/Directory.Packages.props Updates Hot Chocolate/CookieCrumble package versions to v16 RC.
src/Service/Startup.cs Migrates DateTime scalar options and GraphQL endpoint options/Nitro handling to v16 APIs.
src/Core/Services/GraphQLSchemaCreator.cs Removes explicit OneOf enablement now default in v16.
src/Core/Services/ExecutionHelper.cs Updates scalar runtime conversions (Base64String, Duration) and selection syntax node access.
src/Core/Services/DetermineStatusCodeMiddleware.cs Migrates from WithContextData to setting ContextData directly.
src/Core/Resolvers/Sql Query Structures/SqlQueryStructure.cs Updates selection syntax node access for v16 selection changes.
src/Core/Resolvers/CosmosQueryStructure.cs Updates selection type and syntax node access for v16 selection changes.
src/Service.GraphQLBuilder/Sql/SchemaConverter.cs Updates default value literal creation to new scalar APIs and Base64StringType.
src/Service.GraphQLBuilder/GraphQLTypes/DefaultValueType.cs Replaces obsolete ByteArrayType with Base64StringType.
src/Service.GraphQLBuilder/GraphQLStoredProcedureBuilder.cs Updates scalar literal creation APIs for stored proc default values.
src/Service.GraphQLBuilder/CustomScalars/SingleType.cs Migrates custom scalar overrides to v16 coercion hooks and result writing.
src/Service.Tests/UnitTests/MultiSourceQueryExecutionUnitTests.cs Updates assertions to v16 result/document traversal types.
src/Service.Tests/Configuration/ConfigurationTests.cs Updates “no runtime config” expectations for v16 request-time option resolution.
src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs Enables nullable annotations to fix CS8632 under warnings-as-errors.
src/Service.Tests/UnitTests/RequestParserUnitTests.cs Enables nullable annotations to fix CS8632 under warnings-as-errors.
src/Service.Tests/Configuration/RuntimeConfigLoaderTests.cs Enables nullable annotations to fix CS8632 under warnings-as-errors.
Comments suppressed due to low confidence (1)

src/Service.Tests/Configuration/ConfigurationTests.cs:756

  • The test metadata still says "Throws Application exception" (DataRow DisplayName) and the [TestMethod] description mentions an ApplicationException for the CLI scenario, but the updated assertions now accept DataApiBuilderException(ServiceUnavailable) as well. Please update the DisplayName/description to match the new expected behavior so the test output remains accurate.
        [DataRow(new string[] { }, true, DisplayName = "No config returns 503 - config file flag absent")]
        [DataRow(new string[] { "--ConfigFileName=" }, true, DisplayName = "No config returns 503 - empty config file option")]
        [DataRow(new string[] { }, false, DisplayName = "Throws Application exception")]
        [TestMethod("Validates that queries before runtime is configured returns a 503 in hosting scenario whereas an application exception when run through CLI")]
        public async Task TestNoConfigReturnsServiceUnavailable(

Comment thread src/Core/Resolvers/Sql Query Structures/SqlQueryStructure.cs
Comment thread src/Core/Resolvers/Sql Query Structures/SqlQueryStructure.cs Outdated
Comment thread src/Core/Resolvers/CosmosQueryStructure.cs Outdated
Comment thread src/Core/Resolvers/CosmosQueryStructure.cs
Comment thread src/Service.GraphQLBuilder/GraphQLStoredProcedureBuilder.cs Outdated
Comment thread src/Core/Services/ExecutionHelper.cs
Copy link
Copy Markdown
Contributor

@souvikghosh04 souvikghosh04 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Posting comments so far..

Comment thread src/Service.Tests/Configuration/RuntimeConfigLoaderTests.cs Outdated
Comment thread src/Service.Tests/UnitTests/RequestParserUnitTests.cs Outdated
Comment thread src/Service.Tests/UnitTests/SqlQueryExecutorUnitTests.cs Outdated
Comment thread src/Service.GraphQLBuilder/GraphQLTypes/SupportedTypes.cs
Comment thread src/Core/Services/ExecutionHelper.cs Outdated
Comment thread src/Core/Services/ExecutionHelper.cs
Comment thread src/Service/Startup.cs Outdated
Comment thread src/Service/Startup.cs
Comment thread src/Service/Startup.cs
Comment thread src/Core/Services/GraphQLSchemaCreator.cs Outdated
Comment thread src/Core/Services/GraphQLSchemaCreator.cs Outdated
Copy link
Copy Markdown
Collaborator

@Aniruddh25 Aniruddh25 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider optimizing Placeholder query node creation

Comment thread src/Core/Services/GraphQLSchemaCreator.cs
Comment thread src/Core/Services/GraphQLSchemaCreator.cs
Copy link
Copy Markdown
Collaborator

@Aniruddh25 Aniruddh25 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving with suggestions. Thanks for the change

Copy link
Copy Markdown
Contributor

@souvikghosh04 souvikghosh04 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for addressing the comments

@souvikghosh04 souvikghosh04 enabled auto-merge (squash) May 15, 2026 03:08
@souvikghosh04 souvikghosh04 disabled auto-merge May 15, 2026 03:32
@aaronburtle aaronburtle enabled auto-merge (squash) May 15, 2026 22:48
@aaronburtle aaronburtle merged commit c6ecc3c into main May 16, 2026
12 checks passed
@aaronburtle aaronburtle deleted the dev/aaronburtle/HC-16-upgrade branch May 16, 2026 02:55
@github-project-automation github-project-automation Bot moved this from Review In Progress to Done in Data API builder May 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Upgrade DAB to HC version 16.0.0

7 participants