Skip to main content
Version: Current

Migration from v0.X

Note: this page is still in progress!

Package name#

We decided to merge two existing packages @graphql-modules/core and @graphql-modules/di into a single package graphql-modules. There's no regression in terms of bundle size, because core was importing di anyway.

Making GraphQL Modules a single package should improve the developer experience.

import { gql, createModule, Injectable } from 'graphql-modules';

DocumentNode for typeDefs#

v0 accepted string as typeDefs, while v1 doesn't.

import { gql, createModule } from 'graphql-modules';
const myModule = createModule({
/// ...
typeDefs: gql`
type Query {
foo: String
}
`,
});

Classes to Utility Functions#

You no longer need to create new GraphQLModule(), you can use createModule instead:

const myModule = createModule({ ... });

Resolvers Composition => Middleware#

With version 0.x we had resolvers-composition, which was our way to define wrappers for resolvers.

In v1, we changed the name to Middlewares, and changed the API a bit, to make it more flexible and more compatible with similar middlewares implementations:

To define:

function yourMiddleware({ root, args, context, info }, next) {
/* code */
return next();
}

To use:

const myModule = createModule({
// ...
middlwares: {
Query: {
me: [yourMiddleware],
},
},
});

Module/Application Structure#

With version 0.x of GraphQL Modules, we were trying to make modules more dynamic, and build a hierarchy tree of dependencies between modules. This wasn't a great solution for all use-cases, and made things complicated.

In v1, we changed behavior modules to be flat, and added Application to define the root of injection.

You no longer need to define imports and define strict dependencies between modules, since they are all flatten and merged together by the Application.

const moduleOne = createModule({ ... });
const moduleTwo = createModule({ ... });
const application = createApplication({
modules: [moduleOne, moduleTwo]
})

Shared Injectables#

In V0, you needed to create an Injectable, add it to a module, and then in order to consume it in another module, you needed to make sure you have a dependency (with imports) between your modules. This was very strict and made development harder.

Since we moved to a flatten structure of Application, all you need to do in order to share an Injectable, is just to add global: true:

// Module 1
@Injectable({
global: true
})
class MyProvider {
// ...
}
const moduleOne = createModule({ providers: [MyProvider] });
// Module 2
@Injectable()
class MyOtherProvider {
constructor(myProvider: MyProvider) {
}
}
const moduleTwo = createModule({ ... });

Module Config#

In V0, we had the concept of ModuleConfig which was a very specific way to instantiate a module with configuration.

In V1, it's no longer exists, and we believe there are simpler way to do that, with custom tokens.

import { createModule, InjectionToken } from '@graphql-modules/core';
export interface MyModuleConfig {
secretKey: string;
remoteEndpoint: string;
someDbInstance: SomeDBInstance;
}
export const MyConfig = new InjectionToken<MyModuleConfig>();
export const createMyModule = (config: MyModuleConfig) =>
createModule({
// ...
providers: [
{
provide: MyConfig,
useValue: config,
},
],
});
const application = createApplication({
modules: [
createMyModule({
secretKey: '123',
remoteEndpoint: 'http://...',
someDbInstance: db,
}),
],
});

And to consume, that same way as in V0:

@Injectable()
export class MyProvider {
constructor(@Inject(MyConfig) private config: MyModuleConfig) {
// ...
}
}

Session#

In v0, we had a concept of Session to manage the execution of each operation. In v1, we dropped it, in favor of a simpler solution.

Internally, we are using Node's async_hooks to manage a context of an execution, which allow us to share Injectables between Singleton and Operation scope.

Context#

In v0, you could create a context per each module. In v1, context is external for GraphQL-Modules and it's not directly in use. You can do whatever you want with that, and just access it in GraphQL-Modules if you need, but we no longer require you to do specific things with your context.

With v1, you can manage your own context without anything special that needs to be done in order to GraphQL-Modules to work.

You can inject CONTEXT in order to get access to the global execution context, and Modules you create doesn't take part in building that object.

Dependency Injection Scopes#

We decided to reduce the number of Scopes (from 3 to only 2) and change the names. The Session and Request scopes are a bit misleading and hard to understand at first glimpse.

In v1 it's much simpler, there are two Scopes:

  • Singleton - Injectable is instantiated once, at bootstrap phase (createApplication)
  • Operation - Injectable is instantiated once per GraphQL Operation.

In our opinion, it's much easier to reason about.

The ProviderScope was renamed to just Scope.

Dependency Injection Hierarchy#

With v1, the structure of your application is now flat, meaning there's an application level on top of a module level (many).

Application -> [ Module, Module, Module ]

This change enables an abstraction that was not possible in v0. Your modules can depend on Injectables or InjectionTokens provided by Application.

We recommend to read "Hierarchical Injectors" chapter.