Documentation: https://chillicream.com/docs/hotchocolate/v15
Setup
- Use the HotChocolate template OR import HotChocolate packages.
- Queries, mutations and subscriptions are methods in a class with an attribute.
General Notes
- Basically works with every data retrieval logic — you still have to code the retrieval logic.
- Queries and other types support dependency injection and grab services from registered ones.
- Everything is methodes or class, heavy use of attributes.
- Every behavior can be set/configured/overriden by middleware
Queries / mutations
[QueryType]
public class Query
{
// Add DI dependencies
public List<Data> GetData()
{
// implement
}
// This one will create an optional parameter
public List<Data2> GetDataByFilter(string? filter)
{
// implement
}
}
App / Builder Setup
builder.Services
.AddGraphQLServer() // enable
.AddTypes(); // register classes with attributes
- Error handling is handled by the library (field validation).
Subscriptions
Declare a subscription:
public class Subscription
{
[Subscribe]
public Book TrucAdded([EventMessage] Truc truc) => truc;
}
Enable subscriptions:
builder.Services
// ...
.AddInMemorySubscriptions(); // or another handler (e.g. Redis)
app.UseWebSockets();
Publish events for subscriptions using ITopicEventSender:
public async Task<Result> AddTruc()
{
// do actions
await sender.SendAsync(nameof(Subscription.TrucAdded), truc);
}
- Subscription topic can be set dynamically with attributes accepting a pattern.
Scaling subscriptions
Subscriptions are limited by server, as each one require a socket connection. Scaling to allow more suscription force use to use a ditributed mechanisme to broadscast events to all servers. Redis could be enough.
Documentation also talks about using ISubscriptionEventPublisher, a background service to handle events, an a service bus/external topic.
Middleware
- Middleware allows you to handle/intercept requests as with HTTP.
- Can be declared as classes or delegates in your query types.
- Ordering is important (sorting, filtering). Keep in mind the order of declaration and the logic applied — register paging before filtering, as paging is applied on the response and will be executed after.
Directives
- Class inheriting
DirectiveType. - Register it via attribute.
- Either configure its name/scope with an attribute or by overriding
Configure. - You can register a DTO class alongside to give it parameters.
protected override void Configure(
IDirectiveTypeDescriptor<MyDto> descriptor)
In the Configure method, use descriptor.Use to declare its behavior as middleware:
descriptor.Use((next, directive) => context =>
{
// Here, scoped on field: replace the field content.
context.Result = "Bar";
return next.Invoke(context);
});
Resolvers :
- Generic function that can fetch data for a particular field
- Can be either the whole query, a middleware setup on a specific field, a class for which the usage if configured with
ResolveWith - Allow acces to arguments, services,
HttpContext, Parent object (via the[Parent]attribute set on the parameter)
Security
- Authentication is supported out of the box.
- Authorization also.
- Both can use attributes on fields/queries.
- Can also declare roles and policies for granular checks.
- This means you can expose an object and limit some fields/queries/mutations to certain users without much hassle in the code.
Tooling
- HotChocolate includes a Nitro UI alongside the GraphQL endpoint for easy testing.