graphql-modules
Version:
Create reusable, maintainable, testable and extendable GraphQL modules
150 lines (149 loc) • 5.89 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createApplication = createApplication;
const schema_1 = require("@graphql-tools/schema");
const di_1 = require("../di");
const errors_1 = require("../shared/errors");
const utils_1 = require("../shared/utils");
const di_2 = require("./di");
const context_1 = require("./context");
const execution_1 = require("./execution");
const subscription_1 = require("./subscription");
const apollo_1 = require("./apollo");
const operation_controller_1 = require("./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
* ]
* })
* ```
*/
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 = di_1.ReflectiveInjector.resolve((0, di_1.onlySingletonProviders)(providers));
const appInjector = di_1.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 = di_1.ReflectiveInjector.resolve((0, di_1.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 = (0, di_2.createGlobalProvidersMap)({
modules,
scope: di_1.Scope.Singleton,
});
const operationGlobalProvidersMap = (0, di_2.createGlobalProvidersMap)({
modules,
scope: di_1.Scope.Operation,
});
(0, di_2.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 = (0, utils_1.flatten)(modules.map((mod) => mod.typeDefs));
const resolvers = modules.map((mod) => mod.resolvers).filter(utils_1.isDefined);
const schema = (applicationConfig.schemaBuilder || schema_1.makeExecutableSchema)({
typeDefs,
resolvers,
});
const contextBuilder = (0, context_1.createContextBuilder)({
appInjector,
appLevelOperationProviders: appOperationProviders,
modulesMap: modulesMap,
singletonGlobalProvidersMap,
operationGlobalProvidersMap,
});
const createOperationController = (0, operation_controller_1.operationControllerCreator)({
contextBuilder,
});
const createSubscription = (0, subscription_1.subscriptionCreator)({ contextBuilder });
const createExecution = (0, execution_1.executionCreator)({ contextBuilder });
const createSchemaForApollo = (0, apollo_1.apolloSchemaCreator)({
createSubscription,
contextBuilder,
schema,
});
const createApolloExecutor = (0, apollo_1.apolloExecutorCreator)({
createExecution,
});
(0, di_2.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 errors_1.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 errors_1.ModuleNonUniqueIdError(`Modules with non-unique ids: ${collisions.join(', ')}`, `All modules should have unique ids, please locate and fix them.`);
}
}