graphql-component
Version:
Build, customize and compose GraphQL schemas in a componentized fashion
365 lines • 30.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const federation_1 = require("@apollo/federation");
const graphql_1 = require("graphql");
const merge_1 = require("@graphql-tools/merge");
const utils_1 = require("@graphql-tools/utils");
const schema_1 = require("@graphql-tools/schema");
const stitch_1 = require("@graphql-tools/stitch");
const mock_1 = require("@graphql-tools/mock");
/**
* GraphQLComponent class for building modular GraphQL schemas
* @template TContextType - The type of the context object
* @implements {IGraphQLComponent}
*/
class GraphQLComponent {
_schema;
_types;
_resolvers;
_mocks;
_imports;
_context;
_dataSources;
_dataSourceOverrides;
_pruneSchema;
_pruneSchemaOptions;
_federation;
_dataSourceContextInject;
_transforms;
_transformedSchema;
_middleware = [];
constructor({ types, resolvers, mocks, imports, context, dataSources, dataSourceOverrides, pruneSchema, pruneSchemaOptions, federation, transforms }) {
this._types = Array.isArray(types) ? types : [types];
this._resolvers = bindResolvers(this, resolvers);
this._mocks = mocks;
this._federation = federation;
this._transforms = transforms;
this._dataSources = dataSources || [];
this._dataSourceOverrides = dataSourceOverrides || [];
this._dataSourceContextInject = createDataSourceContextInjector(this._dataSources, this._dataSourceOverrides);
this._pruneSchema = pruneSchema;
this._pruneSchemaOptions = pruneSchemaOptions;
this._imports = imports && imports.length > 0 ? imports.map((i) => {
if (!i) {
throw new Error('Import cannot be undefined or null');
}
// Check if it's already a config object (has 'component' property)
if ('component' in i && i.component) {
const importConfiguration = i;
if (this._federation === true) {
importConfiguration.component.federation = true;
}
return importConfiguration;
}
// Otherwise, treat it as an IGraphQLComponent and wrap it
const component = i;
if (this._federation === true) {
component.federation = true;
}
return { component };
}) : [];
this._context = async (globalContext) => {
//BREAKING: The context injected into data sources won't have data sources on it
const ctx = {
dataSources: globalContext.dataSources || {}
};
// Add this component's dataSources if not already present or if empty
if (!globalContext.dataSources || Object.keys(globalContext.dataSources).length === 0) {
Object.assign(ctx.dataSources, this._dataSourceContextInject(globalContext));
}
// Only process imports if they exist
if (this._imports.length > 0) {
// Process imports in parallel if they're independent
const importPromises = this._imports.map(async ({ component }) => {
const importContext = await component.context(globalContext);
return importContext;
});
const importResults = await Promise.all(importPromises);
// Merge results efficiently
for (const { dataSources, ...importedContext } of importResults) {
Object.assign(ctx.dataSources, dataSources);
Object.assign(ctx, importedContext);
}
}
// Handle namespace context if present
if (context) {
if (!ctx[context.namespace]) {
ctx[context.namespace] = {};
}
const namespaceContext = await context.factory.call(this, globalContext);
Object.assign(ctx[context.namespace], namespaceContext);
}
return ctx;
};
this.validateConfig({ types, imports, mocks, federation });
}
get context() {
// Cache middleware array to avoid recreation
const contextFn = async (context) => {
// Inject dataSources early so middleware can access them
const dataSources = this._dataSourceContextInject(context);
// Also gather data sources from imported components for middleware
const importedDataSources = {};
if (this._imports.length > 0) {
for (const { component } of this._imports) {
// Use the public dataSources getter and manually inject them
const componentDataSourcesArray = component.dataSources;
const componentDataSourceOverrides = component.dataSourceOverrides;
const componentInjector = createDataSourceContextInjector(componentDataSourcesArray, componentDataSourceOverrides);
const componentDataSources = componentInjector(context);
Object.assign(importedDataSources, componentDataSources);
}
}
// Combine all data sources for middleware
const allDataSources = Object.assign({}, dataSources, importedDataSources);
let processedContext = Object.assign({}, context, { dataSources: allDataSources });
// Apply middleware more efficiently
if (this._middleware.length > 0) {
for (const mw of this._middleware) {
processedContext = await mw.fn(processedContext);
}
}
const componentContext = await this._context(processedContext);
// More efficient object composition
return Object.assign({}, processedContext, componentContext);
};
contextFn.use = (name, fn) => {
if (typeof name === 'function') {
fn = name;
name = 'unknown';
}
this._middleware.push({ name: name, fn: fn });
return contextFn;
};
return contextFn;
}
get name() {
return this.constructor.name;
}
get schema() {
try {
if (this._schema) {
return this._schema;
}
let makeSchema;
if (this._federation) {
makeSchema = federation_1.buildFederatedSchema;
}
else {
makeSchema = schema_1.makeExecutableSchema;
}
if (this._imports.length > 0) {
// iterate through the imports and construct subschema configuration objects
const subschemas = this._imports.map((imp) => {
const { component, configuration = {} } = imp;
return {
schema: component.schema,
...configuration
};
});
// construct an aggregate schema from the schemas of imported
// components and this component's types/resolvers (if present)
this._schema = (0, stitch_1.stitchSchemas)({
subschemas,
typeDefs: this._types,
resolvers: this._resolvers,
mergeDirectives: true
});
}
else {
const schemaConfig = {
typeDefs: (0, merge_1.mergeTypeDefs)(this._types),
resolvers: this._resolvers
};
this._schema = makeSchema(schemaConfig);
}
if (this._transforms) {
this._schema = this.transformSchema(this._schema, this._transforms);
}
if (this._mocks !== undefined && typeof this._mocks === 'boolean' && this._mocks === true) {
// if mocks are a boolean support simply applying default mocks
this._schema = (0, mock_1.addMocksToSchema)({ schema: this._schema, preserveResolvers: true });
}
else if (this._mocks !== undefined && typeof this._mocks === 'object') {
// else if mocks is an object, that means the user provided
// custom mocks, with which we pass them to addMocksToSchema so they are applied
this._schema = (0, mock_1.addMocksToSchema)({ schema: this._schema, mocks: this._mocks, preserveResolvers: true });
}
if (this._pruneSchema) {
this._schema = (0, utils_1.pruneSchema)(this._schema, this._pruneSchemaOptions);
}
return this._schema;
}
catch (error) {
throw new Error(`Failed to create schema for component ${this.name}: ${error.message}`);
}
}
get types() {
return this._types;
}
get resolvers() {
return this._resolvers;
}
get imports() {
return this._imports;
}
get dataSources() {
return this._dataSources;
}
get dataSourceOverrides() {
return this._dataSourceOverrides;
}
set federation(flag) {
this._federation = flag;
}
get federation() {
return this._federation;
}
dispose() {
this._schema = null;
this._types = null;
this._resolvers = null;
this._imports = null;
this._dataSources = null;
this._dataSourceOverrides = null;
}
transformSchema(schema, transforms) {
if (this._transformedSchema) {
return this._transformedSchema;
}
const functions = {};
const mapping = {};
for (const transform of transforms) {
for (const [key, fn] of Object.entries(transform)) {
if (!mapping[key]) {
functions[key] = [];
mapping[key] = function (...args) {
let result;
while (functions[key].length) {
const mapper = functions[key].shift();
result = mapper(...args);
if (!result) {
break;
}
}
return result;
};
}
functions[key].push(fn);
}
}
this._transformedSchema = (0, utils_1.mapSchema)(schema, mapping);
return this._transformedSchema;
}
validateConfig(options) {
if (options.federation && !options.types) {
throw new Error('Federation requires type definitions');
}
if (options.mocks && typeof options.mocks !== 'boolean' && typeof options.mocks !== 'object') {
throw new Error('mocks must be either boolean or object');
}
}
}
exports.default = GraphQLComponent;
// For backward compatibility
module.exports = GraphQLComponent;
/**
* Wraps data sources with a proxy that intercepts calls to data source methods and injects the current context
* @param {IDataSource[]} dataSources
* @param {IDataSource[]} dataSourceOverrides
* @returns {DataSourceInjectionFunction} a function that returns a map of data sources with methods that have been intercepted
*/
const createDataSourceContextInjector = (dataSources, dataSourceOverrides) => {
const intercept = (instance, context) => {
return new Proxy(instance, {
get(target, key) {
if (typeof target[key] !== 'function' || key === instance.constructor.name) {
return target[key];
}
const original = target[key];
return function (...args) {
return original.call(instance, context, ...args);
};
}
});
};
return (context = {}) => {
const proxiedDataSources = {};
// Inject data sources
for (const dataSource of dataSources) {
proxiedDataSources[dataSource.name || dataSource.constructor.name] = intercept(dataSource, context);
}
// Override data sources
for (const dataSourceOverride of dataSourceOverrides) {
proxiedDataSources[dataSourceOverride.name || dataSourceOverride.constructor.name] = intercept(dataSourceOverride, context);
}
return proxiedDataSources;
};
};
/**
* memoizes resolver functions such that calls of an identical resolver (args/context/path) within the same request context are avoided
* @param {string} parentType - the type whose field resolver is being
* wrapped/memoized
* @param {string} fieldName - the field on the parentType whose resolver
* function is being wrapped/memoized
* @param {function} resolve - the resolver function that parentType.
* fieldName is mapped to
* @returns {function} a function that wraps the input resolver function and
* whose closure scope contains a WeakMap to achieve memoization of the wrapped
* input resolver function
*/
const memoize = function (parentType, fieldName, resolve) {
const _cache = new WeakMap();
return function _memoizedResolver(_, args, context, info) {
const path = info && info.path && info.path.key;
const key = `${path}_${JSON.stringify(args)}`;
let cached = _cache.get(context);
if (cached && cached[key]) {
return cached[key];
}
if (!cached) {
cached = {};
}
const result = resolve(_, args, context, info);
cached[key] = result;
_cache.set(context, cached);
return result;
};
};
/**
* make 'this' in resolver functions equal to the input bindContext
* @param {Object} bind - the object context to bind to resolver functions
* @param {Object} resolvers - the resolver map containing the resolver
* functions to bind
* @returns {Object} - an object identical in structure to the input resolver
* map, except with resolver function bound to the input argument bind
*/
const bindResolvers = function (bindContext, resolvers = {}) {
const boundResolvers = {};
for (const [type, fields] of Object.entries(resolvers)) {
// dont bind an object that is an instance of a graphql scalar
if (fields instanceof graphql_1.GraphQLScalarType) {
boundResolvers[type] = fields;
continue;
}
if (!boundResolvers[type]) {
boundResolvers[type] = {};
}
for (const [field, resolver] of Object.entries(fields)) {
if (['Query', 'Mutation'].indexOf(type) > -1) {
boundResolvers[type][field] = memoize(type, field, resolver.bind(bindContext));
}
else {
// only bind resolvers that are functions
if (typeof resolver === 'function') {
boundResolvers[type][field] = resolver.bind(bindContext);
}
else {
boundResolvers[type][field] = resolver;
}
}
}
}
return boundResolvers;
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxtREFBMEQ7QUFDMUQscUNBQStFO0FBRS9FLGdEQUFxRDtBQUNyRCxnREFPOEI7QUFDOUIsa0RBQTZEO0FBQzdELGtEQUFzRDtBQUN0RCw4Q0FBK0Q7QUE4Ri9EOzs7O0dBSUc7QUFDSCxNQUFxQixnQkFBZ0I7SUFDbkMsT0FBTyxDQUFnQjtJQUN2QixNQUFNLENBQWE7SUFDbkIsVUFBVSxDQUFnQztJQUMxQyxNQUFNLENBQW1CO0lBQ3pCLFFBQVEsQ0FBa0M7SUFDMUMsUUFBUSxDQUFrQjtJQUMxQixZQUFZLENBQWdCO0lBQzVCLG9CQUFvQixDQUFnQjtJQUNwQyxZQUFZLENBQVU7SUFDdEIsbUJBQW1CLENBQW9CO0lBQ3ZDLFdBQVcsQ0FBVTtJQUNyQix3QkFBd0IsQ0FBOEI7SUFDdEQsV0FBVyxDQUFnQjtJQUNuQixrQkFBa0IsQ0FBZ0I7SUFDbEMsV0FBVyxHQUFzQixFQUFFLENBQUM7SUFFNUMsWUFBWSxFQUNWLEtBQUssRUFDTCxTQUFTLEVBQ1QsS0FBSyxFQUNMLE9BQU8sRUFDUCxPQUFPLEVBQ1AsV0FBVyxFQUNYLG1CQUFtQixFQUNuQixXQUFXLEVBQ1gsa0JBQWtCLEVBQ2xCLFVBQVUsRUFDVixVQUFVLEVBQ2U7UUFFekIsSUFBSSxDQUFDLE1BQU0sR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUM7UUFFckQsSUFBSSxDQUFDLFVBQVUsR0FBRyxhQUFhLENBQUMsSUFBSSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBRWpELElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO1FBRXBCLElBQUksQ0FBQyxXQUFXLEdBQUcsVUFBVSxDQUFDO1FBRTlCLElBQUksQ0FBQyxXQUFXLEdBQUcsVUFB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