Search code examples
gographqlgqlgen

Golang gqlgen, fetching separate fields in a single DB query via Dataloading


I am trying to see if its possible to take several fields that have their own resolver and somehow get them in a single DB query since they are fetched from the same place.

Eg. the schema

type ExampleResposne {
  A: Int!
  B: Int!
  C: Int!
}

The gqlgen.yml

models:
  ExampleResponse:
    fields:
      A:
        resolver: true
      B:
        resolver: true
      C:
        resolver: true

And now somehow I'd like to fetch A and B from a same DB call if they are requested at the same time

I have an API that returns these based on the keys

    exampleRes, err := resolver.service.GetExample(ctx, GetExampleRequest{
        Keys: []ExampleKey{
            AKey,
            BKey,
        },
    })

Is something like this possible or should I rethink my approach?

The generated resolvers

func (r *exampleResponseResolver) A(ctx context.Context, obj *model.ExampleResponse) (int, error) { panic(fmt.Errorf("not implemented"))
}

func (r *exampleResponseResolver) B(ctx context.Context, obj *model.ExampleResponse) (int, error) { panic(fmt.Errorf("not implemented"))
}

func (r *exampleResponseResolver) C(ctx context.Context, obj *model.ExampleResponse) (int, error) { panic(fmt.Errorf("not implemented"))
}

Solution

  • It seems you may have some topics confused:


    Dataloading

    The concept of dataloading is to batch multiple requests for data under one resolver's function / method call. This should be considered at the level of the resolving type and not at the level of individual fields of that type.

    An example of this (taken from https://gqlgen.com/reference/dataloaders/) would be to resolve for multiple Users under one database request.


    Graphql Field Resolvers

    With your gqlgen.yml configuration, you have specified individual resolvers for fields with:

    models:
      ExampleResponse:
        fields:
          A:
            resolver: true
          ...
    

    The concept of field resolvers aim to resolve individual fields of a type rather than to resolve the type as a whole, essentially having the opposite effect to what dataloading has. This is useful when we are trying to resolve argumented fields or code splitting resolver logic.

    To note, field resolvers can often cause N+1 problems when not used effectively.


    The Solution

    You might not be seeing a resolver for your type ExampleResponse because you need to append your queries entry-point under the Query type.

    schema.graphql:

    type Query {
        getExample: ExampleResposne
    }
    

    Then run: go run github.com/99designs/gqlgen generate

    You should see:

    func (r *queryResolver) GetExample(ctx context.Context) (*model.ExampleResponse, error) { 
        panic(fmt.Errorf("not implemented"))
    }
    

    See https://graphql.org/learn/schema/#the-query-and-mutation-types for details.