Dry Effects with Dry Container (dependency injection)

Hi Folks question here on using Dry Effects with Auto Inject and Container. I have a situation where I am providing a dependency to a series of HTTP requests where each request needs to be authenticated with a JWT token. Right now each http request by default will open the connection to the auth endpoint and get a valid JWT, but for chained requests, this causes a lot of extra JWT requests. I could re-write my injection to provide a singleton instance of the authenticated connection, but I was also thinking I could provide it as an effect and that might be a nicer solution. I’m stumbling a little on how to actually do that though if your app isn’t built explicitly on middleware. Here is a subset of my code:

module Documents
  class ServiceAccount
    include Dry::Monads[:result]
    include Import["service_account_token", "box_client"]

    def call
      service_account_token.get_token.bind(box_client)
    end
end

module Documents
  class Container
     register "service_account" do
      ServiceAccount.new
    end
  end

  Import = Dry::AutoInject(Container)
end

so inside some handler I can do:

include Import["service_account"]
...
connection = yield service_account.call
result = yield connection.some_client_method

inside tests, I just stub the container dependency.

The effects documentation says that its compatible with the container, but I’m a little unsure on how to take the existing code here and move towards providing that dependency as an effect since most of the examples assume a class you new up in the middleware, and the only place my container is used and an instance is created is presumably the auto inject import constructor: Import = Dry::AutoInject(Container)

Thanks in advance!

Hi, I am just wondering, if you can’t authorize first and pass the token/ decoded token data as a dependency?

The idea of JWT is that it shoud contain the required information to authorize specific actions so I just wonder if you focus on the right part of the problem.

Hi @swilgosz thanks for the reply, the issue is not so much authorization, but rather just caching the token and then having to worry about the token getting stale/expired. I thought that maybe providing it as an effect would allow me to have more sequence control over when the token and account get initiated so I could handle stale tokens more easily. For the moment I’ve handled this by caching the token in the service account, pretty close to the strategy you suggested. Now, in my container:

     register "service_account" do
      # new ServiceAccount constructor will yield the instance
      # but it remains callable to all consumers of the dependency
      ServiceAccount.new { |a| a.connect }
    end

Given that our target environment are AWS Lambda invocations, its likely I will never hit a stale token, but I have some instrumentation to let me know if its ever an issue on a warm lambda.

1 Like

jeez, this tab has been open in my browser for almost a month now :see_no_evil:
I didn’t read it carefully (sorry!) but to use dry-effects with dry-auto_inject you’ll need to use dry-effects’ strategy for injection: dry-effects/auto_inject_spec.rb at bc9d6d03db25e6edb11f16ae0a3ab6e1f0e09d53 · dry-rb/dry-effects · GitHub
I would suggest using a reader effect for your particular task, it doesn’t look like a dependency, more like a runtime context. The mechanics are similar, no question about it, but the semantics are slightly different.

1 Like