Skip to content
Back to skills

Hotchocolate Specialist

ASecurity

Deep HotChocolate v15 GraphQL server expertise — schema design, resolvers, filtering/sorting/projections, pagination, subscriptions, error handling, authorization, performance, persisted operations, source generators, and migration. Self-learns from official docs. Triggers on: HotChocolate, GraphQL filtering/sorting/projections, pagination, subscriptions, mutation conventions, persisted queries, cost analysis, custom scalars, GraphQL authorization, interceptors, source generator, AddTypes, Ho...

  • 17 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 30, 2026
documentationgonodeazuretestinggitapibackendsecurityperformancedocumentation

Works with

  • cursor
  • api

Security analysis

A100/100

Scanned May 30, 2026

npx -y skills add gabrielmoreira/agent-skills-mirror --skill hotchocolate-specialist --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Hotchocolate Specialist?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Hotchocolate Specialist
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/gabrielmoreira-hotchocolate-specialist/badge)](https://www.skillsdirectory.com/skills/gabrielmoreira-hotchocolate-specialist)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: hotchocolate-specialist
description: "Deep HotChocolate v15 GraphQL server expertise — schema design, resolvers, filtering/sorting/projections, pagination, subscriptions, error handling, authorization, performance, persisted operations, source generators, and migration. Self-learns from official docs. Triggers on: HotChocolate, GraphQL filtering/sorting/projections, pagination, subscriptions, mutation conventions, persisted queries, cost analysis, custom scalars, GraphQL authorization, interceptors, source generator, AddTypes, HotChocolate migration, Relay node, UseProjection, UsePaging, UseFiltering, UseSorting."
---

# HotChocolate Specialist

Deep HotChocolate v15 API expertise. Complements the `backend-developer` skill (project-specific patterns) with framework-level knowledge and self-learning from official documentation.

> **Scope boundary**: Project-specific patterns (ObjectType layout, TypeExtension conventions, DataLoader naming, middleware testing, MassTransit, MongoDB) live in the `backend-developer` skill. This skill covers HotChocolate framework APIs, advanced features, and official best practices.

## Self-Learning Workflow

**Before answering any non-trivial HotChocolate question**, fetch the latest official documentation to ensure accuracy. HotChocolate evolves rapidly — never rely solely on training data.

### Step 1: Identify the Topic

Map the user's question to one or more documentation areas from the [Documentation Index](references/doc-index.md).

### Step 2: Fetch Latest Documentation

Use `fetch_webpage` to retrieve the relevant page(s):

```
Primary:   https://chillicream.com/docs/hotchocolate/v15/{topic-path}
Fallback:  https://github.com/ChilliCream/graphql-platform/blob/main/website/src/docs/hotchocolate/v15/{topic-path}/index.md
```

For bleeding-edge features or source code questions, search the GitHub repository:
```
https://github.com/ChilliCream/graphql-platform/tree/main/src/HotChocolate/{module}
```

### Step 3: Cross-Reference with Project Patterns

After fetching official docs, verify alignment with project conventions from `backend-developer` skill and `general.instructions.md`. Prefer project conventions when they intentionally diverge from defaults.

### Step 4: Apply and Cite

Implement using the latest API. Always cite the documentation URL when introducing patterns the user may not have seen before.

## Topic Quick Reference

| Area | Key APIs / Attributes | Doc Path |
|---|---|---|
| **Schema Design** | `ObjectType<T>`, `InterfaceType<T>`, `UnionType`, `EnumType`, `InputObjectType<T>`, `DirectiveType<T>` | `defining-a-schema/` |
| **Scalars** | Built-in + custom `ScalarType<TRuntimeType, TLiteral>` | `defining-a-schema/scalars` |
| **Enums** | `EnumType<T>`, `[GraphQLName]`, `[GraphQLDescription]` | `defining-a-schema/enums` |
| **Interfaces** | `InterfaceType<T>`, `[InterfaceObject]`, `implements` | `defining-a-schema/interfaces` |
| **Unions** | `UnionType`, `UnionType<T>`, annotation-based | `defining-a-schema/unions` |
| **Directives** | `DirectiveType<T>`, `[Directive]`, executable/type-system | `defining-a-schema/directives` |
| **Relay** | `[Node]`, `[ID]`, Global Object Identification, `INodeResolver` | `defining-a-schema/relay` |
| **OneOf** | `[OneOf]` input types | `defining-a-schema/input-object-types` |
| **Dynamic Schemas** | Runtime type building, `ITypeModule` | `defining-a-schema/dynamic-schemas` |
| **Versioning** | `@deprecated`, schema evolution strategies | `defining-a-schema/versioning` |
| **Resolvers** | Pure resolvers, resolver pipeline, `[Parent]`, `[Service]`, `[ScopedService]` | `fetching-data/resolvers` |
| **DataLoader** | `BatchDataLoader`, `GroupedDataLoader`, `CacheDataLoader`, source-generated | `fetching-data/dataloader` |
| **Pagination** | `[UsePaging]`, `[UseOffsetPaging]`, cursor vs offset vs keyset | `fetching-data/pagination` |
| **Filtering** | `[UseFiltering]`, `IFilterConvention`, custom filter fields | `fetching-data/filtering` |
| **Sorting** | `[UseSorting]`, `ISortConvention`, custom sort fields | `fetching-data/sorting` |
| **Projections** | `[UseProjection]`, `IProjectionConvention`, first-class MongoDB support | `fetching-data/projections` |
| **Subscriptions** | `[Subscribe]`, `[Topic]`, in-memory, Redis provider | `defining-a-schema/subscriptions` |
| **Mutations** | Mutation conventions, `MutationConventionOptions`, `[Error]`, `[UseMutationConvention]` | `defining-a-schema/mutations` |
| **Error Handling** | `IErrorFilter`, mutation error conventions, `IError`, `ErrorBuilder` | `defining-a-schema/mutations` |
| **Server Config** | Endpoints, warmup, command-line | `server/` |
| **Interceptors** | `IHttpRequestInterceptor`, `ISocketSessionInterceptor` | `server/interceptors` |
| **DI** | `[Service]`, `RegisterService`, scoped services in resolvers | `server/dependency-injection` |
| **Global State** | `IResolverContext.ContextData`, `SetGlobalState`, `GetGlobalState` | `server/global-state` |
| **Introspection** | Enable/disable, `AllowIntrospection` | `server/introspection` |
| **File Upload** | `IFile`, `Upload` scalar | `server/files` |
| **Batching** | Request batching, variable batching | `server/batching` |
| **Instrumentation** | `IExecutionDiagnosticEvents`, OpenTelemetry integration | `server/instrumentation` |
| **Auth** | `[Authorize]`, policies, roles, `@authorize` directive | `security/` |
| **Persisted Ops** | Automatic Persisted Operations (APQ), file/blob/Redis stores | `performance/` |
| **Cost Analysis** | Query cost/complexity limits, `[Cost]` | Related to CostAnalysis module |
| **Source Generators** | `[Module]`, `AddTypes()`, automatic type registration | `defining-a-schema/#automatic-type-registration` |
| **Migration** | v13→v14→v15 breaking changes and migration paths | `migrating/` |

## Advanced Patterns

### Custom Scalars

```csharp
public class UrlType : ScalarType<Uri, StringValueNode>
{
    public UrlType() : base("Url") { }

    protected override Uri ParseLiteral(StringValueNode valueSyntax)
        => new(valueSyntax.Value);

    protected override StringValueNode ParseValue(Uri runtimeValue)
        => new(runtimeValue.AbsoluteUri);

    public override IValueNode ParseResult(object? resultValue)
        => ParseValue((Uri)resultValue!);
}
```

Register: `.AddType<UrlType>()`

### Filtering — Custom Convention

```csharp
public class CustomFilterConvention : FilterConvention
{
    protected override void Configure(IFilterConventionDescriptor descriptor)
    {
        descriptor.AddDefaults();
        descriptor.Provider(new QueryableFilterProvider(p =>
            p.AddDefaultFieldHandlers()));
    }
}

// Registration
services.AddGraphQLServer()
    .AddFiltering<CustomFilterConvention>();
```

### Sorting — Custom Convention

```csharp
public class CustomSortConvention : SortConvention
{
    protected override void Configure(ISortConventionDescriptor descriptor)
    {
        descriptor.AddDefaults();
        descriptor.Provider(new QueryableSortProvider(p =>
            p.AddDefaultFieldHandlers()));
    }
}
```

### Pagination — Cursor with MongoDB

```csharp
[UsePaging(IncludeTotalCount = true, MaxPageSize = 100, DefaultPageSize = 25)]
[UseProjection]
[UseSorting]
[UseFiltering]
public IExecutable<MyEntity> GetEntities([Service] IMongoCollection<MyEntity> collection)
    => collection.AsExecutable();
```

Order matters: `[UsePaging]` → `[UseProjection]` → `[UseSorting]` → `[UseFiltering]` (outermost to innermost).

### Subscriptions

```csharp
// In mutation resolver
[UseMutationConvention]
public async Task<MyEntity> CreateEntity(
    CreateEntityInput input,
    [Service] ITopicEventSender sender,
    CancellationToken ct)
{
    MyEntity entity = /* create */;
    await sender.SendAsync(nameof(OnEntityCreated), entity, ct);
    return entity;
}

// Subscription
[Subscribe]
[Topic(nameof(OnEntityCreated))]
public MyEntity OnEntityCreated([EventMessage] MyEntity entity) => entity;
```

### Error Handling — Mutation Conventions

```csharp
// Define domain errors as classes
public class EntityNotFoundError
{
    public EntityNotFoundError(string id) => Id = id;
    public string Id { get; }
    public string Message => $"Entity '{Id}' not found.";
}

// Annotate mutation with expected errors
[Error<EntityNotFoundError>]
[Error<ValidationError>]
[UseMutationConvention]
public async Task<MyEntity> UpdateEntity(UpdateEntityInput input, ...)
{
    // throw or return error types
}
```

### Authorization — Field-Level

```csharp
[Authorize(Policy = "AdminOnly")]
public class AdminQueries
{
    [Authorize(Roles = ["superadmin"])]
    public async Task<SensitiveData> GetSensitiveData(...)
        => /* ... */;
}
```

### Interceptors

```csharp
public class CustomHttpRequestInterceptor : DefaultHttpRequestInterceptor
{
    public override ValueTask OnCreateAsync(
        HttpContext context,
        IRequestExecutor requestExecutor,
        OperationRequestBuilder requestBuilder,
        CancellationToken ct)
    {
        // Add custom context data from HTTP headers
        string? tenantId = context.Request.Headers["X-Tenant-Id"];
        requestBuilder.SetGlobalState("TenantId", tenantId);
        return base.OnCreateAsync(context, requestExecutor, requestBuilder, ct);
    }
}

// Registration
services.AddGraphQLServer()
    .AddHttpRequestInterceptor<CustomHttpRequestInterceptor>();
```

### Source Generators & Registration

HotChocolate 12.7+ source generator auto-discovers types decorated with `[QueryType]`, `[MutationType]`, `[SubscriptionType]`, `[ObjectType]`, `[ExtendObjectType]`, etc.

```csharp
// ModuleInfo.cs — triggers source generation
[assembly: Module("MyDomain")]

// Startup — register all discovered types
services.AddGraphQLServer()
    .AddTypes();  // Registers all source-generated types from the module
```

### Persisted Operations

```csharp
services.AddGraphQLServer()
    .UseAutomaticPersistedOperationPipeline()
    .AddFileSystemOperationDocumentStorage("./persisted-operations");
```

Alternative stores: Redis (`AddRedisOperationDocumentStorage`), Azure Blob Storage.

## Troubleshooting Checklist

| Symptom | Likely Cause | Action |
|---|---|---|
| `Type 'X' is not registered` | Missing `AddTypes()` or manual registration | Check `[Module]` attribute + startup config |
| Filtering/sorting not applied | Missing `[UseFiltering]`/`[UseSorting]` or wrong attribute order | Verify attribute stacking order |
| N+1 queries in resolver | Missing DataLoader | Implement `BatchDataLoader` per `backend-developer` conventions |
| Subscription not firing | `ITopicEventSender` not called or topic mismatch | Verify `SendAsync` topic matches `[Topic]` |
| Auth returning `AUTH_NOT_AUTHENTICATED` | Missing `AddAuthorization()` or JWT config | Verify auth pipeline in startup |
| Pagination returns no `totalCount` | `IncludeTotalCount = false` (default) | Set `[UsePaging(IncludeTotalCount = true)]` |
| Mutation error not in union | Missing `[Error<T>]` attribute | Add error type annotation to mutation |
| Schema export changed unexpectedly | Source generator picked up new types | Review `[Module]` scope and type visibility |

## Anti-Patterns

| Anti-Pattern | Why It's Wrong | Fix |
|---|---|---|
| Filtering/sorting on non-indexed fields | Kills DB performance at scale | Ensure MongoDB indexes match filterable/sortable fields |
| `[UseProjection]` without DB support | Projection only works with supported providers | Verify MongoDB/EF Core provider integration |
| Manual pagination implementation | Reinvents cursor/offset logic HC handles | Use `[UsePaging]` or `[UseOffsetPaging]` |
| Catching exceptions in resolvers to return errors | Bypasses HC error pipeline | Use `IErrorFilter` or mutation error conventions |
| Hardcoded `MaxPageSize` > 500 | Opens DoS vector via large queries | Keep `MaxPageSize` ≤ 100 in production |
| Introspection enabled in production | Exposes full schema to attackers | Disable via `AllowIntrospection.Never` or per-policy |
| Ignoring `[Authorize]` on mutations | All mutations default to anonymous | Add authorization explicitly |
| Using `DateTime` scalar for UTC timestamps | Ambiguous timezone semantics | Use `DateTimeOffset` or custom UTC scalar |

## Important Rules

- Always fetch latest documentation before implementing advanced HC features
- Attribute stacking order matters: `[UsePaging]` → `[UseProjection]` → `[UseSorting]` → `[UseFiltering]`
- Never duplicate patterns already defined in `backend-developer` skill — reference it instead
- Cite documentation URLs when introducing new patterns to the team
- Prefer implementation-first (annotation-based) over schema-first approach
- For project-specific conventions (naming, testing), defer to `backend-developer` skill and `tests.instructions.md`

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.