FSharp.HotChocolate
1.0.0
dotnet add package FSharp.HotChocolate --version 1.0.0
NuGet\Install-Package FSharp.HotChocolate -Version 1.0.0
<PackageReference Include="FSharp.HotChocolate" Version="1.0.0" />
<PackageVersion Include="FSharp.HotChocolate" Version="1.0.0" />
<PackageReference Include="FSharp.HotChocolate" />
paket add FSharp.HotChocolate --version 1.0.0
#r "nuget: FSharp.HotChocolate, 1.0.0"
#:package FSharp.HotChocolate@1.0.0
#addin nuget:?package=FSharp.HotChocolate&version=1.0.0
#tool nuget:?package=FSharp.HotChocolate&version=1.0.0
FSharp.HotChocolate
F# support for Hot Chocolate: option-aware nullability, F# async resolvers, F# collection inputs, and F# unions as GraphQL enums, unions, or interfaces.
FSharp.HotChocolate is intended for implementation-first or code-first schemas that expose F# types directly.
Install
dotnet add package FSharp.HotChocolate
Quick Start
Call AddFSharpSupport() on every schema that uses FSharp.HotChocolate features.
open Microsoft.Extensions.DependencyInjection
open HotChocolate
builder.Services
.AddGraphQLServer()
.AddFSharpSupport()
When migrating from older F# setup, remove calls such as AddFSharpTypeConverters or
AddTypeConverter<OptionTypeConverter>.
Features
- F# nullability and conversion for
Option<_>andValueOption<_> - Hot Chocolate
Optional<_>inputs with F# nullability Async<_>fields and node resolvers- Field resolvers shaped as
CancellationToken -> Task<_>orCancellationToken -> ValueTask<_>, like those used by IcedTasks - F#
list<_>andSet<_>input parameters and input object fields - Fieldless F# unions as automatically inferred GraphQL enum types
- Single-field-case F# unions as GraphQL union or interface types
F# Nullability
For members from F# assemblies, FSharp.HotChocolate treats values as non-null by default and treats Option<_> and
ValueOption<_> as nullable. This applies across fields, arguments, input object fields, directive arguments, and F#
Hot Chocolate type extensions.
The nullability walk understands common wrappers and containers, including Async<_>, Task<_>, ValueTask<_>,
arrays, seq<_>, ResizeArray<_>, list<_>, and Set<_>. Explicit non-null annotations such as
[<GraphQLNonNullType>] or NonNullType<_> inside [<GraphQLType>] are ignored for option-wrapped values.
Optional Inputs
Hot Chocolate Optional<_> keeps its value-state API, but GraphQL nullability comes from the inner F# type:
Optional<string>is exposed as required and non-null (String!), so omitted andnullinputs are rejected.Optional<string option>distinguishes omitted, explicitnull, and a concrete string value.
Opt Out
Apply [<SkipFSharpNullability>] to an assembly, type, interface, member, or parameter to use Hot Chocolate's normal
nullability rules for that scope.
[<SkipFSharpNullability>]
type LegacyInput = { Text: string }
type Query() =
[<SkipFSharpNullability>]
member _.LegacyField(x: string) = x
member _.MixedField([<SkipFSharpNullability>] legacyText: string, fsharpText: string option) =
fsharpText |> Option.defaultValue legacyText
Async and Cancellation
Fields and node resolvers may return Async<_>. FSharp.HotChocolate starts the computation with the request's
RequestAborted cancellation token. If you need different cancellation behavior, convert the computation to Task<_>
yourself.
Resolvers can also return a function shaped as CancellationToken -> Task<_> or
CancellationToken -> ValueTask<_> when the resolver needs direct access to the request cancellation token. This is the
shape used by IcedTasks cancellable task builders.
Input Collections
Input parameters and input object fields can use F# list<_> or Set<_>. The converters support GraphQL variables,
empty collections, converted element types, nullable elements, and option/value-option-wrapped collection shapes.
F# Unions
FSharp.HotChocolate supports three GraphQL shapes for F# unions.
Fieldless Unions As Enums
Public fieldless F# unions referenced by the schema are automatically inferred as GraphQL enum types after
AddFSharpSupport() in supported input and output contexts, unless an explicit compatible Hot Chocolate type already
applies. The enum mapping respects configured Hot Chocolate naming conventions, [<GraphQLName>],
[<GraphQLIgnore>], [<EnumType>], and XML doc comments.
type Color =
| Red
| [<GraphQLName("custom_green")>] Green
| [<GraphQLIgnore>] InternalOnly
You can still register a descriptor explicitly when you need customization:
type ColorDescriptor() =
inherit FSharpUnionAsEnumDescriptor<Color>()
override _.Configure(descriptor: IEnumTypeDescriptor<Color>) =
base.Configure(descriptor)
descriptor.Name("PaintColor") |> ignore
builder.Services
.AddGraphQLServer()
.AddFSharpSupport()
.AddType<ColorDescriptor>()
Single-Field Cases As GraphQL Unions
Use FSharpUnionAsUnionDescriptor<'Union> when each case has exactly one payload field. Case names are not used; the
GraphQL union is made from the payload object types.
type SearchResult =
| Book of Book
| Author of Author
builder.Services
.AddGraphQLServer()
.AddFSharpSupport()
.AddType<FSharpUnionAsUnionDescriptor<SearchResult>>()
Use [<GraphQLType>] on a case when the inferred payload object type is not the schema type you want, for example when
you have multiple GraphQL object types for the same runtime type.
type SearchResult =
| [<GraphQLType(typeof<BookPreviewType>)>] Book of Book
| Author of Author
Single-Field Cases As GraphQL Interfaces
Use FSharpUnionAsInterfaceDescriptor<'Union> when each case has exactly one payload field and the payload object types
should implement a shared GraphQL interface.
type Book = { Title: string }
type Author = { Name: string }
type Node =
| Book of Book
| Author of Author
member this.DisplayName =
match this with
| Book book -> book.Title
| Author author -> author.Name
builder.Services
.AddGraphQLServer()
.AddFSharpSupport()
.AddType<FSharpUnionAsInterfaceDescriptor<Node>>()
Shared interface fields should be normal instance members on the F# union, including intrinsic F# type augmentations that compile as union instance members. Fields that are specific to one payload object type can be added with normal Hot Chocolate configuration, including object type extensions:
[<ExtendObjectType(typeof<Book>)>]
type BookExtensions() =
member _.TitleLength([<Parent>] book: Book) = book.Title.Length
builder.Services
.AddGraphQLServer()
.AddFSharpSupport()
.AddType<FSharpUnionAsInterfaceDescriptor<Node>>()
.AddTypeExtension<BookExtensions>()
By default, interface fields are inferred from eligible public members declared on the F# union. Compiler-generated
members such as Tag, IsBook, and comparison helpers are ignored. For manual field binding, inherit from the
descriptor and pass BindingBehavior.Explicit:
type NodeDescriptor() =
inherit FSharpUnionAsInterfaceDescriptor<Node>(BindingBehavior.Explicit)
override _.Configure(descriptor: IInterfaceTypeDescriptor<Node>) =
base.Configure(descriptor)
descriptor.Field(fun n -> n.DisplayName :> obj) |> ignore
Interface descriptor rules:
- By default, the GraphQL interface is named from the F# union. Use normal Hot Chocolate naming tools such as
[<GraphQLName>]ordescriptor.Name(...)to customize it. - Each case payload object type becomes a GraphQL type that implements that interface.
- Resolvers can return the F# union directly; FSharp.HotChocolate unwraps each case to its payload object.
- Fields inferred from union members, such as
DisplayNameabove, are added to the interface and to each case payload object type automatically. WithBindingBehavior.Explicit, usedescriptor.Field(fun n -> n.DisplayName :> obj)to select union members explicitly. - Fields added by name, such as
descriptor.Field("customField"), or fields added with Hot Chocolate'sInterfaceTypeExtension, are ordinary Hot Chocolate interface fields; FSharp.HotChocolate does not mirror them to the case payload object types that implement the interface. - Per the GraphQL spec, interfaces must define at least one field; field-less marker interfaces are not supported.
[<GraphQLType>]on individual cases overrides the payload object type, like with GraphQL union descriptors.
Returning Unions Through Wrappers
F# union enum, union, and interface values can be returned directly and through supported wrappers such as Option<_>,
ValueOption<_>, arrays, Task<_>, ValueTask<_>, and Async<_>.
Limitations
- Multi-schema apps: Hot Chocolate stores wrapper type definitions in a process-wide registry. If any schema in a
process calls
AddFSharpSupport(), every schema in that process that exposes supported F# wrapper types such asOption<_>,ValueOption<_>, orAsync<_>should also callAddFSharpSupport(). Otherwise, those wrapper types can be unwrapped without the schema-specific handling from this package. - With global object identification,
Option-wrappedIDvalues inside lists are not supported (#6). - With
[<UsePaging>], the nullability offirst,last,before, andafteris controlled by Hot Chocolate. These parameters are always nullable in the schema, so explicit method parameters should usually be wrapped inOption<_>. The exception isRequirePagingBoundaries = truewithAllowBackwardPagination = false; then Hot Chocolate effectively enforces non-nullfirst/afterinput even though the schema still shows nullable paging parameters. - Function-shaped cancellable resolvers are not supported together with
[<UsePaging>]. For paged fields, acceptCancellationTokenas a normal field parameter and apply it manually.
Development
dotnet tool restore
dotnet fantomas --check .
dotnet test -c Release -maxCpuCount
Directory.Packages.props controls the Hot Chocolate package versions. Maintainers should ask their AI agent to use the
repo-local
release-fsharp-hotchocolate
skill when releasing, or read that skill directly for manual release details.
Acknowledgements
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- FSharp.Core (>= 6.0.0)
- HotChocolate.Types (>= 16.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 39 | 5/13/2026 |
| 0.2.0 | 14,897 | 6/17/2025 |
| 0.2.0-hc15-001 | 240 | 6/17/2025 |
| 0.1.13 | 61,887 | 11/22/2024 |
| 0.1.13-hc15-001 | 171 | 11/22/2024 |
| 0.1.10 | 308 | 11/18/2024 |
| 0.1.10-hc15-001 | 133 | 11/18/2024 |
| 0.1.9 | 1,557 | 10/19/2024 |
| 0.1.9-hc15-001 | 147 | 10/19/2024 |
| 0.1.8 | 349 | 9/25/2024 |
| 0.1.7 | 196 | 9/25/2024 |
| 0.1.6 | 171 | 9/25/2024 |
| 0.1.5 | 160 | 9/25/2024 |
| 0.1.4 | 160 | 9/24/2024 |
| 0.1.3 | 177 | 9/23/2024 |
| 0.1.2 | 151 | 9/23/2024 |