← Back to all posts

Notes about GraphQL and .NET | Antoine's tech notes

An image showing a network around graphql.

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.