Support for F# types and nullability in HotChocolate.
(
currently not working for HC >= 15.0.0-p.12, see #20)
- Remove any existing calls to
AddFSharpTypeConvertersorAddTypeConverter<OptionTypeConverter>. - Call
AddFSharpSupport:
.AddGraphQLServer().AddFSharpSupport()FSharp.HotChocolate supports the following:
- Idiomatic F# nullability through
Option<_> Async<_>fields- F# collection types on input
- F# unions as GraphQL enums
- F# unions as GraphQL unions
All fields defined in F# (including HotChocolate type extensions for types not defined in F#) will have idiomatic F#
nullability applied. This means that everything except Option-wrapped values will be non-nullable (!) in the GraphQL
schema. Any usages of [<GraphQLNonNullType>], or NonNullType<_> in [<GraphQLType>], will be ignored.
Due to limitations (see below) or other reasons, you may want to opt out of F# nullability for a certain scope. You can
apply the SkipFSharpNullability attribute to parameters, members, types (including HotChocolate type extensions), or
whole assemblies to disable F# nullability processing for that scope.
- When using global object identification,
Option-wrapped node resolvers are not supported (#5). - When using global object identification,
Option-wrappedIDvalues inside lists are not supported (#6). - Support for
ValueOption<_>is not yet added (#10). - When using
UsePaging, the nullability of thefirst,last,before, andafterparameters is controlled by HotChocolate. These are always nullable. Therefore, if these parameters are explicitly present in your method (e.g. if doing custom pagination), make sure you wrap them inOption<_>. The only exception is if you useRequirePagingBoundaries = truewithAllowBackwardPagination = false; in that case, HotChocolate will effectively enforce that these (only) two parameters are non-nullon input (even though they are nullable in the schema), and it's safe to not wrap them inOption<_>in code.
Fields can now be Async<_>.
The computations are automatically wired up to the RequestAborted cancellation token. If you do not want that, please
convert the Async<_> to Task<_> yourself as you see fit.
- When using some built-in middleware such as
[<UsePaging>],Async<_>is not supported for that field. (#8). - When using global object identification,
Async<_>is not supported for node resolvers (#9).
Parameters and input types can now use List<_> or Set<_>.
You can use F# fieldless unions as enum types in GraphQL, similar to how normal enums work:
type MyUnion =
| A
| B
| [<GraphQLName("custom_name")>] C
| [<GraphQLIgnore>] DAdd the type to GraphQL using FSharpUnionAsEnumDescriptor:
AddGraphQLServer().AddType<FSharpUnionAsEnumDescriptor<MyEnum>>()It will give this schema:
enum MyUnion {
A
B
custom_name
}You can inherit from FSharpUnionAsEnumDescriptor to customize the type as usual. Remember to call base.Configure in
your override.
type MyEnumDescriptor() =
inherit FSharpUnionAsEnumDescriptor<MyEnum>()
override this.Configure(descriptor: IEnumTypeDescriptor<MyEnum>) =
base.Configure(descriptor)
descriptor.Name("CustomName") |> ignoreThen, use your subtype in AddType:
AddType<MyEnumDescriptor>()You can define an F# union type to be used as a union in the GraphQL schema:
type MyUnion =
| A of MyTypeA
| B of MyTypeB(The case names are not used.)
Add the type to GraphQL using FSharpUnionAsUnionDescriptor:
AddGraphQLServer().AddType<FSharpUnionAsUnionDescriptor<MyUnion>>()You can then return MyUnion directly through fields:
type Query() =
member _.MyUnion : MyUnion = ...It will give this schema:
type MyTypeA { ... }
type MyTypeB { ... }
type Query { myUnion: MyUnion! }
union MyUnion = MyTypeA | MyTypeBYou can inherit from FSharpUnionAsUnionDescriptor to customize the type as usual. Remember to call base.Configure in
your override.
type MyUnionDescriptor() =
inherit FSharpUnionAsUnionDescriptor<MyUnion>()
override this.Configure(descriptor) =
base.Configure(descriptor)
descriptor.Name("CustomName") |> ignoreThen, use your subtype in AddType:
AddType<MyUnionDescriptor>()If the default inferred types for the union cases are not correct (for example, if you have multiple GraphQL schema
types for the same runtime type), you can use GraphQLTypeAttribute on individual cases to specify the correct type:
type MyUnion =
| [<GraphQLType(typeof<MyTypeA2Descriptor>)>] A of MyTypeA
| B of BMany thanks to @Stock44 for creating this gist that sparked this project. Without that, I'd have no idea where to even begin.
Two package versions are published: A stable version for the stable version of HotChocolate, and a pre-release version for the pre-release version of HotChocolate.
The compiler constant HC_PRE is available for conditional compilation in all projects. It is defined when building for
the pre-release version of HotChocolate.
- Make necessary changes to the code
- Update the changelog
- Update the versions in the fsproj files:
- If the change only pertains to a pre-release of HotChocolate and only the pre-release package needs to be published,
only adjust
VersionSuffix - Otherwise, bump
VersionPrefixand reset the last part ofVersionSuffixto-001.
- If the change only pertains to a pre-release of HotChocolate and only the pre-release package needs to be published,
only adjust
- Commit and tag the commit (this is what triggers deployment):
- If
VersionPrefixwas bumped, the tag should bev/<prefix>where<prefix>isVersionPrefix, e.g.v/1.0.0 - If only
VersionSuffixwas bumped, the tag should bev/<prefix>-<suffix>, e.g.v/1.0.0-hc15-001
- If
- Push the changes and the tag to the repo. If the build succeeds, the packages are automatically published to NuGet.