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}
module.ts
import { createModule } from 'graphql-modules'import { Data } from './data'export const myModule = createModule({ id: 'my-module', // ... providers: [ { 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'}
keys.ts
import { InjectionToken } from 'graphql-modules'export const ApiKey = new InjectionToken<string>('api-key')
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.
import { Injectable } from 'graphql-modules'import { Auth } from './auth'@Injectable()class Posts { constructor(private auth: Auth) {} allPosts() { if (!this.auth.isLoggedIn()) { throw new Error('Auth required') } return [ // ... ] }}
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() } }}
import { Injectable, Inject } from 'graphql-modules'import { ApiKey } from './keys'@Injectable()class Posts { constructor(@Inject(ApiKey) private key: string) {} allPosts() { if (!this.key) { throw new Error('API key is required') } return [ // ... ] }}
Global Providers and Token
Module can share tokens and providers with other modules, even application. When enabling a global flag, the provider still depends on an original Injector.
// Posts module@Injectable()export class Posts { constructor( private auth: Auth, private logger: Logger ) {} getMyPosts() { const me = this.auth.getCurrentUser() logger.debug(`Asking for my posts`) // ... }}
In the scenario above, we’ve got two modules: Posts and Users.
Both modules define their own Logger.
Users module defines Auth service as globally available.
Posts module defines Posts service.
When Posts.getMyPosts() is called, it fetches a current user from Auth service.
The Auth.getCurrentUser() calls a logger (provided by Users module).
In Posts.getMyPosts() a different logger is invoked (provided by Posts module).
What does all of that mean? Because global providers are accessible in all modules, they still use the injector they were created by.
Global providers are still isolated but their API is public to other modules.
Lazy with forwardRef
The forwardRef function allows referring to references that 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 the current module.
This site uses cookies for analytics and improving your experience.