Version: next

Providers and Tokens

The building blocks of DI are Providers and Tokens.

  • Token - symbol or class representing an object or any value in Dependency Injection space.
  • Provider - provides a value to a token

Usage Requirements

GraphQL Modules depend on Reflect API, please install and import reflect-metadata before every other module.

import 'reflect-metadata';
/* code */

Defining Tokens

Dependency Injection is an abstraction over actual objects and values.

There are three kinds of providers:

Class

Using a class provider in GraphQL Modules is the easiest approach to DI. The class is instantiated automatically and in case of Operation Scope it's created only on demand.

Every Service should be decorated with @Injectable as follows:

data.ts
import { Injectable } from 'graphql-modules';
@Injectable()
export class Data {}
module.ts
import { createModule } from 'graphql-modules';
import { Data } from './data';
export const myModule = createModule({
id: 'my-module',
/* ... */
providers: [Data],
});

It's a shorthand expression for:

{
provide: Data,
useClass: Data,
}

Value

Value provider requires a Token that represents a value, either InjectionToken or a class.

{
provide: ApiKey,
useValue: 'my-api-key',
}

Factory

In case you want to create a dependent value, using a factory provider is the answer. Factory can be useful also to create an instance of a class, for example when using third-party libraries.

{
provide: ApiKey,
useFactory(config: Config) {
if (config.environment === 'production') {
return 'my-api-key';
}
return null;
},
deps: [Config]
}

Using Services and Tokens

Provider is a way to define a value and match it with a Token or a Service. Let's see how to consume Services and Tokens.

Service

Accessing a service is fairly simple. You ask for a service in a constructor of a class or by using Injector directly.

import { Auth } from './auth';
const resolvers = {
Query: {
me(parent, args, context, info) {
const auth = context.injector.get(Auth);
return auth.getCurrentUser();
},
},
};

Injector is available in GraphQL Context under injector property.

InjectionToken

Consuming InjectionToken is very similar to Service. The only difference is that you need to use @Inject decorator but only in some cases.

import { ApiKey } from './keys';
const resolvers = {
Query: {
me(parent, args, context, info) {
const apiKey = context.injector.get(ApiKey);
if (!this.key) {
throw new Error('API key is required');
}
return auth.getCurrentUser();
},
},
};

Injector is available in GraphQL Context under injector property.

Lazy with forwardRef

The forwardRef function allows to refer to references which are not yet defined. Useful when circular dependency of modules is an issue.

import { Injectable, Inject } from 'graphql-modules';
import { ApiKey } from './keys';
@Injectable()
class Posts {
constructor(@Inject(forwardRef(() => ApiKey)) private key: string) {}
allPosts() {
if (!this.key) {
throw new Error('API key is required');
}
return [
/* ... */
];
}
}

Available Tokens

GraphQL Modules have a set of built-in and ready to use Tokens. They may be handy in some situations.

  • CONTEXT - represents a provided GraphQL Context (GraphQLModules.GlobalContext)
  • MODULE_ID - represents an id of a module