Type-graphql: Inject Dependency Into Resolver Without Using DI Framework

Created on 8 Nov 2019  路  7Comments  路  Source: MichalLytek/type-graphql

Describe the issue
I would like to inject a few dependencies into a resolver, but I do not want to use a dependency injection framework.

Are you able to make a PR that fix this?
If necessary I can work on it, but I assume this is a documentation gap.

Additional context
If I just need to inject one or two objects into a resolver, then I shouldn't need to import an entire DI framework to solve this. If type-graphql expects certain DI APIs, it would be nice to see a guide for how to mock a container and pass this into buildSchema

Question Solved

Most helpful comment

Is there any reason I can't just provide the dependency via a function arg? It feels weird I am forced to involve this framework

All 7 comments

It is possible to pass dependencies to resolvers without using typedi.

import Container from 'typedi'

const dep1 = new Dep1()
const dep2 = new Dep2()

const myResolver = new MyResolver(dep1, dep2);

Container.set(MyResolver, myResolver);

const schema = await buildSchema({
    [myResolver, /* other resolvers */],
    Container,
    ...
})

But I guess there is no way around using typedi for injecting the resolvers into buildSchema() but using a Container.

Using dependency injection without container is not a good idea as you're asking yourself into maintenance problems.

However, TypeGraphQL has no requirements about the used container - it's not coupled with typedi, inversify-js or other libs. All you have to provide is an object with a get method that receive the resolver class and should return an instance of the class.

So in your case, it might look like this:

const dep1 = new Dep1()
const dep2 = new Dep2()
const myResolver = new MyResolver(dep1, dep2);

const MyContainer = {
  get(ResolverClass) {
    if (ResolverClass === MyResolver) {
      return myResolver;
    }
    return new ResolverClass();
  }
};

const schema = await buildSchema({
  resolvers: [MyResolver, OtherResolver],
  container: MyContainer,
});

So it will use the provided instance for selected resolver, otherwise it will create a new instance of the class. You can enhance the example with some caching like in DefaultContainer.

Thanks! This answers the question I had.

Is there any reason I can't just provide the dependency via a function arg? It feels weird I am forced to involve this framework

via a function arg

What do you mean?

If you want to use dependency injection pattern (provide dependencies into constructor), just use a DI container like the simple and easy to use TypeDI. There's no sense to resolve the deps chain manually, it's in a long term a maintenance hell.

via a function arg

What do you mean?

If you want to use dependency injection pattern (provide dependencies into constructor), just use a DI container like the simple and easy to use TypeDI. There's no sense to resolve the deps chain manually, it's in a long term a maintenance hell.

Something like:

const schema = await buildSchema({
  resolvers: [new MyResolver(new Dep1(), new Dep2()), new OtherResolver()],
});

Is there any reason I can't just provide the dependency via a function arg?

So I will reverse the question - is there any reason you can just use DI container to resolve the dependencies? 馃槈

resolvers: [new MyResolver(new Dep1(), new Dep2()), new OtherResolver()],

This syntax would really complicate the default container that is supposed to be simple:
https://github.com/MichalLytek/type-graphql/blob/877f197dfa8d4d2d7890a38dfc0bf1a548febcc8/src/utils/container.ts#L18-L30

I would have to reinvent the wheel and provide a functionality that is equal to Container.set(MyResolver, new MyResolver(new Dep1(), new Dep2()) - I need to detect if the provided resolver value is function (class) or object (class instance), then read the constructor to have a reference to the class and then register it in instances of the default container.

And it's against design goal - default container is just a "placeholder" if you don't need dependency injection, and if you need, you should use a container library or your simple custom solution that I've showed.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

reilem picture reilem  路  3Comments

limenutt picture limenutt  路  3Comments

robertchung97 picture robertchung97  路  3Comments

Janushan picture Janushan  路  3Comments

laukaichung picture laukaichung  路  3Comments