Migration from v0.X
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({
// ...
middlewares: {
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 no longer exists, and we believe there is a 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.
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 the 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.