graphql#extendSchema TypeScript Examples

The following examples show how to use graphql#extendSchema. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: faderation-factory.util.ts    From nestjs-mercurius with MIT License 5 votes vote down vote up
/**
 * Inspired by https://github.com/mercurius-js/mercurius/blob/master/lib/federation.js#L231
 * Accept a GraphQLSchema to transform instead of a plain string containing a graphql schema
 * @param schema
 */
export function transformFederatedSchema(schema: GraphQLSchema) {
  // FIXME remove this dependency
  // but graphql#printSchema does not print necessary federation directives
  const { printSubgraphSchema } = loadPackage(
    '@apollo/subgraph',
    'FederationFactory',
    () => require('@apollo/subgraph'),
  );

  // Workaround for https://github.com/mercurius-js/mercurius/issues/273
  const schemaString = printSubgraphSchema(schema)
    .replace('type Query {', 'type Query @extends {')
    .replace('type Mutation {', 'type Mutation @extends {')
    .replace('type Subscription {', 'type Subscription @extends {');

  schema = extendSchema(schema, parse(FEDERATION_SCHEMA), {
    assumeValidSDL: true,
  });

  if (!schema.getType('Query')) {
    schema = new GraphQLSchema({
      ...schema.toConfig(),
      query: new GraphQLObjectType({
        name: 'Query',
        fields: {},
      }),
    });
  }

  schema = extendSchema(
    schema,
    parse(`
    extend type Query {
      _service: _Service!
    }
  `),
    {
      assumeValid: true,
    },
  );

  const query = schema.getType('Query') as GraphQLObjectType;
  const queryFields = query.getFields();

  queryFields._service = {
    ...queryFields._service,
    resolve: () => ({ sdl: schemaString }),
  };

  const entityTypes = Object.values(schema.getTypeMap()).filter(
    (type) => isObjectType(type) && typeIncludesDirective(type, 'key'),
  );

  if (entityTypes.length > 0) {
    schema = extendSchema(
      schema,
      parse(`
      union _Entity = ${entityTypes.join(' | ')}
      extend type Query {
        _entities(representations: [_Any!]!): [_Entity]!
      }
    `),
      {
        assumeValid: true,
      },
    );

    const query = schema.getType('Query') as GraphQLObjectType;
    const queryFields = query.getFields();
    queryFields._entities = {
      ...queryFields._entities,
      resolve: (_source, { representations }, context, info) => {
        return representations.map((reference) => {
          const { __typename } = reference;

          const type = info.schema.getType(__typename);
          if (!type || !isObjectType(type)) {
            throw new MER_ERR_GQL_GATEWAY_INVALID_SCHEMA(__typename);
          }

          const resolveReference = (type as any).resolveReference
            ? (type as any).resolveReference
            : function defaultResolveReference() {
                return reference;
              };

          const result = resolveReference(reference, {}, context, info);

          if (result && 'then' in result && typeof result.then === 'function') {
            return result.then((x) => addTypeNameToResult(x, __typename));
          }

          return addTypeNameToResult(result, __typename);
        });
      },
    };
  }

  return schema;
}
Example #2
Source File: index.ts    From graphql-mesh with MIT License 5 votes vote down vote up
transformSchema(schema: GraphQLSchema) {
    const additionalTypeDefs =
      this.typeDefs &&
      loadTypedefsSync(this.typeDefs, {
        cwd: this.baseDir,
        loaders: [new CodeFileLoader(), new GraphQLFileLoader()],
      });
    const baseSchema = additionalTypeDefs ? extendSchema(schema, additionalTypeDefs[0].document) : schema;

    const transformedSchema = mapSchema(baseSchema, {
      [MapperKind.COMPOSITE_FIELD]: (
        fieldConfig: GraphQLFieldConfig<any, any>,
        currentFieldName: string,
        typeName: string
      ) => {
        const fieldKey = `${typeName}.${currentFieldName}`;
        const newFieldConfig = this.replacementsMap.get(fieldKey);
        if (!newFieldConfig) {
          return undefined;
        }

        const fieldName = newFieldConfig.name || currentFieldName;
        const targetFieldName = newFieldConfig.field;
        const targetFieldConfig = selectObjectFields(
          baseSchema,
          newFieldConfig.type,
          fieldName => fieldName === targetFieldName
        )[targetFieldName];

        if (newFieldConfig.scope === 'config') {
          const targetResolver = targetFieldConfig.resolve;
          targetFieldConfig.resolve = newFieldConfig.composer(targetResolver);

          // replace the entire field config
          return [fieldName, targetFieldConfig];
        }

        // override field type with the target type requested
        fieldConfig.type = targetFieldConfig.type;

        // If renaming fields that don't have a custom resolver, we need to map response to original field name
        if (newFieldConfig.name && !fieldConfig.resolve) fieldConfig.resolve = source => source[currentFieldName];

        if (newFieldConfig.scope === 'hoistValue') {
          // implement value hoisting by wrapping a default composer that hoists the value from resolver result
          fieldConfig.resolve = defaultHoistFieldComposer(fieldConfig.resolve || defaultFieldResolver, targetFieldName);
        }

        // wrap user-defined composer to current field resolver or, if not preset, defaultFieldResolver
        fieldConfig.resolve = newFieldConfig.composer(fieldConfig.resolve || defaultFieldResolver);

        // avoid re-iterating over replacements that have already been applied
        this.replacementsMap.delete(fieldKey);

        return [fieldName, fieldConfig];
      },
    });

    return transformedSchema;
  }
Example #3
Source File: schema.ts    From squid with GNU General Public License v3.0 5 votes vote down vote up
export function buildSchema(doc: DocumentNode): GraphQLSchema {
    let schema = extendSchema(baseSchema, doc)
    let errors = validateSchema(schema).filter(err => !/query root/i.test(err.message))
    if (errors.length > 0) {
        throw errors[0]
    }
    return schema
}
Example #4
Source File: index.ts    From graphql-mesh with MIT License 4 votes vote down vote up
async getUnifiedSchema({ rawSources, typeDefs, resolvers, transforms }: MeshMergerContext) {
    this.logger.debug(`Creating localServiceList for gateway`);
    const rawSourceMap = new Map<string, RawSourceOutput>();
    const localServiceList: { name: string; typeDefs: DocumentNode }[] = [];
    const sourceMap = new Map<RawSourceOutput, GraphQLSchema>();
    await Promise.all(
      rawSources.map(async rawSource => {
        const transformedSchema = wrapSchema(rawSource as any);
        rawSourceMap.set(rawSource.name, rawSource);
        sourceMap.set(rawSource, transformedSchema);
        const sdl = await this.store
          .proxy(`${rawSource.name}_sdl`, PredefinedProxyOptions.StringWithoutValidation)
          .getWithSet(async () => {
            this.logger.debug(`Fetching Apollo Federated Service SDL for ${rawSource.name}`);
            const sdlQueryResult: any = await execute({
              schema: transformedSchema,
              document: parse(SERVICE_DEFINITION_QUERY),
            });
            if (sdlQueryResult.errors?.length) {
              throw new AggregateError(sdlQueryResult.errors, `Failed on fetching Federated SDL for ${rawSource.name}`);
            }
            return sdlQueryResult.data._service.sdl;
          });
        localServiceList.push({
          name: rawSource.name,
          typeDefs: parse(sdl),
        });
      })
    );
    this.logger.debug(`Creating ApolloGateway`);
    const gateway = new ApolloGateway({
      localServiceList,
      buildService: ({ name }) => {
        this.logger.debug(`Building federation service: ${name}`);
        const rawSource = rawSourceMap.get(name);
        const transformedSchema = sourceMap.get(rawSource);
        return new LocalGraphQLDataSource(transformedSchema);
      },
      logger: this.logger,
      debug: !!process.env.DEBUG,
      serviceHealthCheck: true,
    });
    this.logger.debug(`Loading gateway`);
    const { schema, executor: gatewayExecutor } = await gateway.load();
    const schemaHash: any = printSchemaWithDirectives(schema);
    let remoteSchema: GraphQLSchema = schema;
    this.logger.debug(`Wrapping gateway executor in a unified schema`);
    remoteSchema = wrapSchema({
      schema: remoteSchema,
      executor: <TReturn>({ document, info, variables, context, operationName }: ExecutionRequest) => {
        const documentStr = printWithCache(document);
        const { operation } = info;
        // const operationName = operation.name?.value;
        return gatewayExecutor({
          document,
          request: {
            query: documentStr,
            operationName,
            variables,
          },
          operationName,
          cache: this.cache,
          context,
          queryHash: documentStr,
          logger: this.logger,
          metrics: {},
          source: documentStr,
          operation,
          schema,
          schemaHash,
          overallCachePolicy: {} as any,
        }) as ExecutionResult<TReturn>;
      },
      batch: true,
    });
    const id$ = this.pubsub.subscribe('destroy', () => {
      gateway.stop().catch(err => this.logger.error(err));
      id$.then(id => this.pubsub.unsubscribe(id)).catch(err => console.error(err));
    });
    this.logger.debug(`Applying additionalTypeDefs`);
    typeDefs?.forEach(typeDef => {
      remoteSchema = extendSchema(remoteSchema, typeDef);
    });
    if (resolvers) {
      this.logger.debug(`Applying additionalResolvers`);
      for (const resolversObj of asArray(resolvers)) {
        remoteSchema = addResolversToSchema({
          schema: remoteSchema,
          resolvers: resolversObj,
          updateResolversInPlace: true,
        });
      }
    }
    if (transforms?.length) {
      this.logger.debug(`Applying root level transforms`);
      remoteSchema = wrapSchema({
        schema: remoteSchema,
        transforms: transforms as any[],
        batch: true,
      });
    }
    this.logger.debug(`Attaching sourceMap to the unified schema`);
    remoteSchema.extensions = remoteSchema.extensions || {};
    Object.defineProperty(remoteSchema.extensions, 'sourceMap', {
      get: () => sourceMap,
    });
    return remoteSchema;
  }