GraphQL Modules lets you use dependency injection among your modules, and lets you inject configs, functions, classes and instances into your modules.
We expose a simple API that covers most use cases concerning backend modules.
We learned not to force using dependency injection too early in the process, because dependency injection makes sense only in some specific use cases, and using it can be recommended only when your codebase is quite large and you need to move fast.
GraphQL Modules lets you choose whether to use dependency injection or not.
Let's start by creating a simple class
UserProvider with the
Now, let's add this
Provider to our
Now, let's implement the
Query.user resolver as a simple function inside
The lifecycle of a
Provideris by default a singleton. So you can use the implemented functions as utility and still use
thisto save global variables.
To use this function from our Provider in the actual resolver implementation, get
Now our resolver is just a proxy to our implementation: we can easily replace
UserProvider with mocks during tests.
We can use
getUserById from other modules.
Import providers from other modules
Provider from other modules, you can just inject it using
Injection tokens are used just for identifying your value and fetching it from the available injectables.
It could be either
MyProvider, do the following:
privateon the argument declaration; it's just a TypeScript trick to declare a class member and set it at the same time, and so now
this.otherProvideris available to use.
If you wish to decouple the actual class implementation and the injection token, you can create your own injection token and declare it this way:
This way, you can ask for the actual value of
MY_CLASS_TOKEN from other providers, without knowing the specific implementation:
This is a very common and useful design pattern related to dependency injection. The power of TypeScript interfaces enables you to use it easily.
You can also create custom
Providers (which are non-classes) with injection tokens.
It's useful when you want your module to get something from outside with a specific signature.
You can use custom injection tokens to identify your injectables.
We have already learned how to provide classes:
just specify the class in the list of
providers in your module declaration.
Or use another class which has same interface.
The lifecycle of a class provider is a singleton by default, i.e. they are created only once and you are using the same instance for all GraphQL executions. But you can change the lifecycle using Provider Scopes.
Value providers are an easy way to pass an existing instance of
class or any other value that you wish to make available to
You can use any value, and attach it to an injection token.
You can use value injectables to inject instances and global injectables, such as
Loggerinstance, database connection/collections, secret tokens etc.
Factories provide another way to pass an instance of
provider. The return value of the fuctory function is used as the value of the provider.
This hook is called once when your application is started.
By defining this hook as a method in your class provider, you can get access to useful information: the top
GraphQLModule instance, GraphQL Context, and the network session.
OnRequesthook is called on each HTTP GraphQL request with a single
OnResponse hook (experimental)
It takes the same parameter as the
OnRequest hook but gets called right before the server sends the HTTP response to the client.
OnResponsehook is called on each HTTP GraphQL request with a single
OnError hook (experimental)
It takes one parameter of the type
Error; it gets called when any error is thrown during execution of resolvers.
This hook is not called if there is an error on dependency injection or context building because then dependency injection completely stops.
This hook is similar to
OnRequest but it is called on the initialization of the WebSocket connection.
It is exactly the same with the
OnConnect hook passed to
subscriptions in SubscriptionServer.
OnConnecthook is called once for each WebSocket GraphQL connection.
This hook is similar to
OnResponse but it is called on the termination of the WebSocket connection.
It is exactly the same as the
OnDisconnect hook passed to
subscriptions in SubscriptionServer.
OnDisconnecthook is called once for each WebSocket GraphQL connection.
OnOperationCompletehooks work similarly to the GraphQL Subscription Server implementation. See also the document of subscriptions-transport-ws.
You can define different lifecycles for your provider. You have three options.
Application Scope (by default)
If you define a provider in this scope (which is default), the provider will be instantiated on the application start and remain the same in the entire application and all the following requests. The providers in this scope can be considered as a shared state across all users’ interactions with our application. It basically means that the instance will be treated as a singleton.
When a network request arrives at your GraphQL server, the GraphQL server calls the context factory of the parent module. The parent module creates a session injector and instantiates session-scoped providers with the session object which contains the current context, session injector and network request. This session object is passed through the module's resolvers using the module's context.
In other words, providers defined in the session scope are constructed at the start of the network request and then kept until the network request is closed. Whereas application-scoped providers are kept during the application runtime and shared between all the following network requests and resolvers in the requests, this type of providers would not be shared between different requests; in resolver calls those belong to the same network request.
This session scope is kept on memory for all the following network requests of the same connection if the connection uses WebSocket. For regular HTTP, it is terminated immediately.
If you have request-scope'd providers in your GraphQL Module, these providers are generated in every injection. This means a request-scoped provider is never kept by neither the application state, nor the session state. So this type of providers works just like a factory. It creates an instance each time you request from the injector.
Every GraphQL-Module creates on each network request a
ModuleSessionInfo instance that contains the raw request from the GraphQL Server, the
SessionInjector with session-scoped instances and application-scoped instances, and the
Context object constructed by
contextBuilder of the module.
Note that you cannot use this built-in provider in the application scope.