Guides

Resolvers

In GraphQL resolvers describe the logic that fetches data for a specific field.

Field resolvers run independently from each other fields and allow the execution engine to parallelize their execution.

This independent approch to fetch data allows us to build powerfull schemas that consists of multiple data sources in a very simple way.

Since, we have two major approaches with Hot Chocolate to define a schema we also have two approaches how to declare our resolvers. We will start by looking at how we can declare resolvers with the schema-first approach and the look at how this is done in the code-first world.

It is important to know that we can mix both approaches. Moreover, resolvers are integrated as a component into the field-middleware-pipeline. A field-middleware is more complex but can also open up more scenarios. One could for instance write a middleware that resolves the data for multiple fields from a creatain well-defined data source.

Since, the class Query is used as our resolver type, the query engine will automatically create an instance of this type as singleton. The lifetime of the resolver object is basically bound to the lifetime of the query executor.

We can also take charge of the lifetime management by registering the resolver type with the dependency injection. In this case the query engine will retrieve the type from the IServiceProvider and not perform any lifetime management.

Sometimes, we do not want to explicitly declare resolvers since we have already modeled our entities very well and just want to map those to our schema. In this case we can just bind our type like the following:

First of all you are able to path in an entity object on which the resolvers are executed. In this case the query engine will do nothing and operate on the passed in entity.

If no initial root value was passed into the query engine, the query engine will create a new Query object by itself. The instance will be disposed (if disposable) after the request was completed.

Like with the resolver type we can take charge of the lifetime by registering the root types as services with our dependency injection.

In the case that we have not specified any resolvers for our bound entity, Hot Chocolate will generate an in-memory assembly that contains the inferred resolvers.

Moreover, we can combine our approach in order to provide specific resolver logic for our entity or in order to extend on our entity. In many cases our entity may just represent part of the data structure that we want to expose in our schema. In this case we can just provide resolver logic to fill the gaps.

In the above case the GetGreetings method has an argument Query which is our bound entity. Resolver methods can specify the original field arguments as specified by the field definition as well as context arguments.

publicstringGetGreetings([Parent]Query query) => query.Greetings;

The ParentAttribute signals the query engine that this argument shall be the instance of the declaring type of our field.

We also could let the query engine inject us the resolver context which provides us with all the context data for our resolver.

For example we could access all the previous resolved object in our path by accessing IResolverContext.Source. Or, we could access scoped context data that were passed down by one of the previous resolvers in the path.

In order to keep our resolver clean and easy to test we can also just let the query engine inject the parts of the resolver context that we really need for our resolver like the ObjectType to which our current field belongs etc.

publicstringGetGreetings(ObjectType type) => type.Name;

Code-First

Code-first is the second approach with which we can be describe a GraphQL schema. In code first field definition and resolver logic are more closely bound together.

The above example declares a type named Query with one field called greetings of the type String that always returns foo. Like with the schema-first approach we can create types that are not explicitly bound to a specific .NET type like in the above example.

The above example class SomeResolvers provides resolvers for multiple types. The types can be declared with the GraphQLResolverOfAttribute either by providing the .NET entity type or by providing the schema type name.

The schema builder will associate the various resolver methods with the correct schema fields and types by analysing the method parameters. We are providing a couple of attributes that can be used to give the resolver method more context like the return type or the description and so on.

Resolver Dependency Injection

Hot Chocolate supports resolver parameter dependency injection. Basically we are able to inject things that we would usually get from the resolver context itself. This makes it clear what demands the resolver has.