Scoped Providers in GraphQL-Modules Dependency Injection

Arda TANRIKULU
Jan 11 · 5 min read
Understand how scopes work in GraphQL-Modules

We recently released a new version of GraphQL-Modules with a new feature called Scoped Providers in the dependency injection system of GraphQL-Modules.

Dependency injection in GraphQL-Modules is optional, and you can use it according to your preference. It provides a solution for writing Providers, which are just classes, you can use to write your business-logic, and this way to separate it from your API declaration, reuse it, and communicate between modules easily.

This new feature allows you to define Providers to be used in different scopes;

Application Scope

If you define a provider in this scope which is default, the provider will be instantiated on application-start and will be 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’s basically means that the instance will be treated as Singleton.

For example you have a provider called ExampleApplicationProvider , then this provider has a counter in it;

And let’s assume that we have a module declaration something like below;

Finally, let’s try to call the following GraphQL Request in multiple times;

In first call, you will get 1 , but in the second call you will get 2 . And in every request this value will be incremented; because Application-Scoped Providers are kept in memory until the application is terminated; and the same instance of that provider will be used on each request. Let’s see other types of providers to understand this one better.

Session Scope

When a network request is arriving to your GraphQL-Server, GraphQL-Server calls the context factory of the parent module. The parent module creates a session injector together with instantiating session-scoped providers with that session object which contains the current context, session injector and network request. This session object is passed through module’s resolvers using module’s context.

In other words, providers defined in the session scope are constructed in the beginning of the network request, then kept until the network request is closed. While application-scoped providers is kept during the application runtime, and shared between all the following network requests and resolvers inside of these requests, this type of providers would not be shared between different requests but in resolver calls those belong to same network request.

Let’s try the same thing in that scope by creating a new provider called ExampleSessionProvider .

Then change our module declaration to use this provider;

So at this time, in every mutation call our Session-Scoped Provider’s increment method will be called and its value will be returned as a result of that mutation.

In first call, you will get 1 , but in the second call you will get 1 again. But why? Because on each request ExampleSessionProvider is instantiated from scratch specifically for that network request which is called session in our DI system. But to see the point of the session scope, let’s assume we have two more resolvers called multiply .
First let’s add another method called multiply that takes one argument to be multiplied by our counter value;

After that, let’s use this new method in our new resolver;

When we call the following GraphQL request,

In first call, the initial counter value 0 will be incremented . Then the result becomes 1 and this value will be returned as a result of increment mutation, but on the same request we have multiply as well. So the same counter value will be multiplied by 2 , and this value will be returned as a result of multiply mutation.

So, on every request the result will be;

{
increment: 1,
multiply: 2
}

But if the provider is in ApplicationScope, the result will be [3,6] in the second call.

Request Scope

If you have request-scoped providers in your GraphQL Module, these providers are generated in every injection. This means a request-scoped provider is never kept neither application state, nor session state. So, this type of providers works just like Factory. It creates an instance each time you request from the injector.

Let’s assume we do the similar changes for RequestScope. The results will be like below on each request;

{
increment: 1,
multiply: 0
}

Because request-scoped providers are constructed on each injector.get call. They are kept in the current function scope, they’re not kept in any session or DI container. That’s why, we can consider them factory .

As you can see the example and the results above, ExampleApplicationProvider is shared between different network requests, while ExampleSessionProvider is shared between resolver-calls inside the same network request. But, ExampleRequestProvider is only kept in the same resolver call.

New ModuleSessionInfo built-in provider

Let’s assume that you have a token required for your authentication process. And this token is probably stored in request header. And GraphQL-Modules provides you access to network request in a built-in provider called ModuleSessionInfo. This session object is the raw request/response object passed by your HTTP Server. For example, if you’re using Express, you will get something like { req: IncommingMessage, res: Response } .

Every GraphQL-Module creates a ModuleSessionInfo instance in each network request that contains raw Session from the GraphQL Server, SessionInjector that contains Session-scoped instances together with Application-scoped ones and Context object which is constructed with contextBuilder of the module. But, notice that you cannot use this built-in provider.

As you can see, you cannot use ModuleSessionInfo in Application Scope, because application-scoped providers belong to the whole application, and application-scoped providers are completely independent from the network request.

However, you can use onRequest hook to have ModuleSessionInfo in your Application-Scoped provider which is called on each network request.

What’s next?

We are constantly trying to improve the set of tools provided by GraphQL-Modules, and we welcome you to try it and share your experience and thoughts.

All posts about GraphQL Modules

  1. GraphQL Modules — Feature based GraphQL Modules at scale
  2. Why is True Modular Encapsulation So Important in Large-Scale GraphQL Projects?
  3. Why did we implement our own Dependency Injection library for GraphQL-Modules?
  4. Scoped Providers in GraphQL-Modules Dependency Injection
  5. Writing a GraphQL TypeScript project w/ GraphQL-Modules and GraphQL-Code-Generator
  6. Authentication and Authorization in GraphQL (and how GraphQL-Modules can help)
  7. Authentication with AccountsJS & GraphQL Modules
  8. Manage Circular Imports Hell with GraphQL-Modules

The Guild

The Guild

Thanks to Dotan Simha

Arda TANRIKULU

Written by

JavaScript Enthusiast, member of The Guild https://github.com/ardatan

The Guild

The Guild

The Guild

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade