1

I want to use dependency injection with graphql-ruby.

I.e.

module CustomerCredits
  module Types
    class QueryType < GraphQL::Schema::Object

      description 'The query root of this schema'

      field :filter_users, [Types::UserType], null: false do
        argument :parameters, InputTypes::UserFilterParameters, required: true
      end

      # resolvers

      def filter_users(arguments) 
        repo = UserRepository.new(complex_arguments) # I want to inject the dependency UserRepository
    
        repo.filtered_users(**arguments[:parameters])
      end

    end
  end
end

Using dependency injection in initialize is not possible, because QueryType is instantiated by graphql-ruby.

wuarmin
  • 3,274
  • 3
  • 18
  • 31
  • You can try something with `instance_eval` but it's tricky. – tadman Feb 05 '21 at 00:05
  • There is no other possibility? – wuarmin Feb 05 '21 at 10:45
  • I'm sure there's an infinite number of possibilities depending on how creative and/or outrageous you want to get. Unfortunately that makes this question way too open-ended. What would help is clarification on out of all this, *what* you're trying to inject. – tadman Feb 05 '21 at 19:50
  • In my example, I want to inject the dependency UserRepository. I want to decouple it from Query Type (but I don't want to use a resolver class) Imagine UserRepository hides an external Api and in test suite I want to double it. – wuarmin Feb 05 '21 at 21:53

2 Answers2

1

As you've mentioned, injection through the initializer might not be super straight forward, so if you want to go fully into dependency injection and inversion of control, you could leverage an IOC Container library like Dry Auto Inject. I know it might be a full blown solution, and it could possibly be too heavy handed for your use case (not sure), but since you're already using repositories in Ruby, it might not be.

Zippie
  • 6,018
  • 6
  • 31
  • 46
  • Thank you for your response. I know dry auto inject. It's great for development and prod envs, but I need the injection in test env. I. e. I have to double the UserRepo because of external Api calls. I can't find a solution with dry auto inject to flexible change one dep in the container for one spec. What do you think? – wuarmin Feb 09 '21 at 10:06
  • 1
    Check this example for testing in the dry container section: https://dry-rb.org/gems/dry-container/0.8/testing/ Does that help? – Zippie Feb 09 '21 at 10:45
1

Following the schema definition from graphql-ruby, one solution I thought of for this problem was to inject your database reference into a controller class, then when your controller is hit, you pass the database reference as part of the context.

# app/controllers/graphql_controller.rb
result = MySchema.execute(
  params[:query],
  variables: params[:variables],
  context: { 
    current_user: current_user, 
    db: my_database_ref # inject database ref here
    },

)
render json: result

Then in your Query Type definiton you can pull the db from the context.

class QueryType < GraphQL::Schema::Object
  description "The query root of this schema"

  field :post, PostType, "Find a post by ID" do
    argument :id, ID
  end

  def post(id:)
    db = context[:db]  # pull db reference from context here
    db[:posts].where(id:).first
  end
end
theUser
  • 1,346
  • 3
  • 21
  • 40