SuperAPI AI Directory

hotchocolate-best-practices

Coding & development

View repository

Updated 10-15 20:12

About this skill

HotChocolate Best Practices

You are implementing or reviewing a HotChocolate GraphQL server in .NET. All guidance targets HotChocolate 16.6 or newer — do not apply it to v15-or-older codebases without checking the migration guides. This skill defines how ChilliCream expects that code to look. It covers implementation — resolvers, data access, DataLoaders, pagination. Schema design (SDL shape, naming, nullability) belongs to the sibling skill graphql-schema-design; use both when a task spans design and implementation.

Core principles

  1. Implementation-first. Write plain C# — static resolver classes with attributes like [QueryType], [MutationType], [ObjectType<T>], [DataLoader] — and let HotChocolate infer the schema. Do not use schema-first SDL binding, and reach for descriptor/Configure overrides only for what attributes cannot express.

  2. All data access flows through DataLoaders. A resolver for a relation (any non-root field that fetches data) never queries a DbContext or repository directly — it calls a DataLoader. Direct queries create N+1 loads and bypass request-scoped caching.

  3. Top-level byId fields use DataLoaders too. Root lookups are batchable: clients batch them with field aliases in a single operation and with request/variable batching across operations. A root resolver that hits the database directly forfeits that coalescing.

  4. DataLoaders are source-generated. Write a static method annotated with [DataLoader]; never hand-write a class deriving from BatchDataLoader/GroupedDataLoader. The generator emits the class, interface, and registration.

  5. Projections flow through QueryContext<T>. Every DataLoader accepts a QueryContext<TValue> parameter so consumers can pass selections down to the database, and always pins its key into the projection (query.Include(x => x.Id)) so the key survives narrowing. The flip side: every resolver that reads parent properties declares them with [Parent(requires: nameof(...))] so projections keep those fields available.

  6. Pagination is cursor-based and ordered. Paginated fields use PagingArguments + ToPageAsync/ToBatchPageAsync; every paginated query has a deterministic order ending in the key, and paging on non-root types goes through a batch-paging DataLoader. Never offset pagination — relative cursors cover jump-to-page needs.

Project setup

Every HotChocolate project references the source generator as an analyzer-only package and names its module explicitly:

<PackageReference Include="HotChocolate.AspNetCore" />
<PackageReference Include="HotChocolate.Data" />
<PackageReference Include="HotChocolate.Types.Analyzers">
  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
  <PrivateAssets>all</PrivateAssets>
</PackageReference>

Properties/ModuleInfo.cs holds one line, naming the module after the domain — never rely on the assembly-name-derived default:

[assembly: Module("RecommendationTypes")]

This specifies the name of the generated method that registers all GraphQL-related types of the project — types and DataLoaders alike. General server settings live in a shared AddDefaultSettings extension so the main chain shows only schema-specific choices — see references/server-setup.md:

builder
    .AddGraphQL()
    .AddDefaultSettings()
    .AddRecommendationTypes();

Reference file index

Detailed rules with worked wrong-vs-right examples. Load only what the current task touches.

Reference When to load
references/dataloaders.md Any DataLoader, any resolver that fetches data, any N+1 concern, any byId field
references/pagination.md Any paginated field, [UseConnection], PagingArguments, connection/page types, sorting on collections
references/resolvers.md Extending entities with [ObjectType<T>], [BindMember], [Parent(requires:)], root type classes, field naming
references/subgraph.md The project is a subgraph — detect via .AddSourceSchemaDefaults() in the builder chain or a schema-settings.json file. Covers [Lookup] on byId fields, entity references, cost enforcement, node-as-lookup
references/server-setup.md New projects, Program.cs / builder chains, csproj packages, [assembly: Module], AddDefaultSettings
references/mutations.md Any mutation, [MutationType], mutation conventions, [Error], payload/input shapes
references/filtering.md Any UseFiltering usage, filter input types, deciding whether a field should be filterable
references/sorting.md Any UseSorting usage, sort input types, deciding whether a field should be client-sortable

More references will be added as the skill grows.