UNPKG

graphql-modules

Version:

Create reusable, maintainable, testable and extendable GraphQL modules

147 lines (146 loc) 5.79 kB
import { makeExecutableSchema } from '@graphql-tools/schema'; import { ReflectiveInjector, onlySingletonProviders, onlyOperationProviders, Scope, } from '../di'; import { ModuleDuplicatedError, ModuleNonUniqueIdError, } from '../shared/errors'; import { flatten, isDefined } from '../shared/utils'; import { createGlobalProvidersMap, attachGlobalProvidersMap, instantiateSingletonProviders, } from './di'; import { createContextBuilder } from './context'; import { executionCreator } from './execution'; import { subscriptionCreator } from './subscription'; import { apolloSchemaCreator, apolloExecutorCreator } from './apollo'; import { operationControllerCreator } from './operation-controller'; /** * @api * Creates Application out of Modules. Accepts `ApplicationConfig`. * * @example * * ```typescript * import { createApplication } from 'graphql-modules'; * import { usersModule } from './users'; * import { postsModule } from './posts'; * import { commentsModule } from './comments'; * * const app = createApplication({ * modules: [ * usersModule, * postsModule, * commentsModule * ] * }) * ``` */ export function createApplication(applicationConfig) { function applicationFactory(cfg) { const config = cfg || applicationConfig; const providers = config.providers && typeof config.providers === 'function' ? config.providers() : config.providers; // Creates an Injector with singleton classes at application level const appSingletonProviders = ReflectiveInjector.resolve(onlySingletonProviders(providers)); const appInjector = ReflectiveInjector.createFromResolved({ name: 'App (Singleton Scope)', providers: appSingletonProviders, }); // Filter Operation-scoped providers, and keep it here // so we don't do it over and over again const appOperationProviders = ReflectiveInjector.resolve(onlyOperationProviders(providers)); const middlewareMap = config.middlewares || {}; // Validations ensureModuleUniqueIds(config.modules); // Create all modules const modules = config.modules.map((mod) => mod.factory({ injector: appInjector, middlewares: middlewareMap, })); const modulesMap = createModulesMap(modules); const singletonGlobalProvidersMap = createGlobalProvidersMap({ modules, scope: Scope.Singleton, }); const operationGlobalProvidersMap = createGlobalProvidersMap({ modules, scope: Scope.Operation, }); attachGlobalProvidersMap({ injector: appInjector, globalProvidersMap: singletonGlobalProvidersMap, moduleInjectorGetter(moduleId) { return modulesMap.get(moduleId).injector; }, }); // Creating a schema, flattening the typedefs and resolvers // is not expensive since it happens only once const typeDefs = flatten(modules.map((mod) => mod.typeDefs)); const resolvers = modules.map((mod) => mod.resolvers).filter(isDefined); const schema = (applicationConfig.schemaBuilder || makeExecutableSchema)({ typeDefs, resolvers, }); const contextBuilder = createContextBuilder({ appInjector, appLevelOperationProviders: appOperationProviders, modulesMap: modulesMap, singletonGlobalProvidersMap, operationGlobalProvidersMap, }); const createOperationController = operationControllerCreator({ contextBuilder, }); const createSubscription = subscriptionCreator({ contextBuilder }); const createExecution = executionCreator({ contextBuilder }); const createSchemaForApollo = apolloSchemaCreator({ createSubscription, contextBuilder, schema, }); const createApolloExecutor = apolloExecutorCreator({ createExecution, }); instantiateSingletonProviders({ appInjector, modulesMap, }); return { typeDefs, resolvers, schema, injector: appInjector, createOperationController, createSubscription, createExecution, createSchemaForApollo, createApolloExecutor, ɵfactory: applicationFactory, ɵconfig: config, }; } return applicationFactory(); } function createModulesMap(modules) { var _a; const modulesMap = new Map(); for (const module of modules) { if (modulesMap.has(module.id)) { const location = module.metadata.dirname; const existingLocation = (_a = modulesMap.get(module.id)) === null || _a === void 0 ? void 0 : _a.metadata.dirname; const info = []; if (existingLocation) { info.push(`Already registered module located at: ${existingLocation}`); } if (location) { info.push(`Duplicated module located at: ${location}`); } throw new ModuleDuplicatedError(`Module "${module.id}" already exists`, ...info); } modulesMap.set(module.id, module); } return modulesMap; } function ensureModuleUniqueIds(modules) { const collisions = modules .filter((mod, i, all) => i !== all.findIndex((m) => m.id === mod.id)) .map((m) => m.id); if (collisions.length) { throw new ModuleNonUniqueIdError(`Modules with non-unique ids: ${collisions.join(', ')}`, `All modules should have unique ids, please locate and fix them.`); } }