Skip to content

Exposing custom GQL Queries

Sometimes we want to expose queries that receives arguments, or that returns a single element or that performs some advanced filtering or grouping before returning the elements.

In that case, the ExposeInPublicApi will not suffice. We need to create a class ,very similar to an App Service, in the sense that it is application level logic.

These classes, called Queries, will usually apply specifications and return an IQueryable.

For example, this query returns the current position assigned to a user:

C#
public class PositionQuery : IQuery
{
    [UseSuiteSingleOrDefault]
    public Task<IQueryable<Position>> GetPositionForUser(
        Guid userCorrelationId,
        [Service] IQueryableFactory factory,
        IResolverContext context)
    {
        var aggregate = AggregateSpecification.For<Position>()
            .AndFilter(new PositionForAssigneeFilterSpecification(userCorrelationId));
        return factory.Execute(aggregate);
    }
}

Unfortunately, attributes are required, at least for now. When returning a single element, you have to add [UseSuiteSingleOrDefault].

This is very important, since it is what makes the SQL SELECT only what we are selecting in the GQL query.

You'll notice that the method returns a Task<IQueryable<Position>>, you can obtain it using the IQueryableFactory in combination with an specification, as shown in the example.

Note

We recommend using specifications and the IQueryableFactory instead of using the DbContext directly, as the IQueryableFactory will take care of obtaining the proper DbSet from the correct DbContext.

With this simple trick we can generate all sorts of queries.

This is a second example that returns a list of elements instead of a single one. The only difference, besides the logic, is that we use [UseSuiteList(typeof(Position))] instead of [UseSuiteSingleOrDefault]

C#
public class PositionQuery : IQuery
{
    [UseSuiteList(typeof(Position))]
    public Task<IQueryable<Position>> GetPositionsOf(
        Guid? parentPositionId,
        [Service] IQueryableFactory factory,
        IResolverContext context)
    {
        var aggregate = AggregateSpecification.For<Position>()
            .AndFilter(new ChildPositionFilterSpecification(parentPositionId));
        return factory.Execute(aggregate);
    }
}

The above query is equivalent to the one we would get by exposing it through the module API in terms of the response type since [UseSuiteList] provides the connection type syntax.