@nestjs/graphql
Version:
Nest - modern, fast, powerful node.js web framework (@graphql)
69 lines (68 loc) • 3.39 kB
JavaScript
import { mergeTypeDefs } from '@graphql-tools/merge';
import { loadPackage } from '@nestjs/common/utils/load-package.util.js';
import { Kind, parse } from 'graphql';
import { gql } from 'graphql-tag';
import { GraphQLDefinitionsFactory } from '../graphql-definitions.factory.js';
import { extend } from '../utils/index.js';
const DEFAULT_ROOT_TYPE_NAMES = {
query: 'Query',
mutation: 'Mutation',
subscription: 'Subscription',
};
/**
* @publicApi
*/
export class GraphQLFederationDefinitionsFactory extends GraphQLDefinitionsFactory {
async exploreAndEmit(typePaths, path, outputAs, isDebugEnabled, definitionsGeneratorOptions, typeDefs) {
const typePathDefs = await this.gqlTypesLoader.mergeTypesByPaths(typePaths);
const mergedTypeDefs = extend(typePathDefs, typeDefs);
const { buildSubgraphSchema, printSubgraphSchema, } = await loadPackage('@apollo/subgraph', 'ApolloFederation', () => import('@apollo/subgraph'));
const typeDefsDocument = gql `
${mergedTypeDefs}
`;
const schema = buildSubgraphSchema([
{
typeDefs: typeDefsDocument,
resolvers: {},
},
]);
const subgraphDocument = this.omitFederationDefinitions(parse(printSubgraphSchema(schema)), typeDefsDocument);
// "buildSubgraphSchema" generates an empty Query definition if there is the `extend type Query` statement
// This leads to duplicated IQuery interfaces
// see: https://github.com//issues/2344
const mergedDefinition = mergeTypeDefs([subgraphDocument], {
useSchemaDefinition: false,
throwOnConflict: true,
commentDescriptions: true,
reverseDirectives: true,
});
const tsFile = await this.gqlAstExplorer.explore(gql `
${mergedDefinition}
`, path, outputAs, definitionsGeneratorOptions);
await tsFile.save();
this.printMessage(`[${new Date().toLocaleTimeString()}] The definitions have been updated.`, isDebugEnabled);
}
/**
* Since v2.15, "printSubgraphSchema" prints the entire subgraph schema, including
* the types, root fields and schema definition that federation adds on top of
* the user-provided type definitions. Drop everything the type definitions
* don't declare themselves so the generated output matches earlier versions.
*/
omitFederationDefinitions(subgraphDocument, typeDefsDocument) {
const declaredNames = new Set(typeDefsDocument.definitions.map((definition) => this.getDefinitionName(definition)));
const definitions = subgraphDocument.definitions.filter((definition) => {
if (definition.kind === Kind.SCHEMA_DEFINITION) {
// Same as "printSchema", omit the schema definition when it only uses the default root type names
return definition.operationTypes.some(({ operation, type }) => type.name.value !== DEFAULT_ROOT_TYPE_NAMES[operation]);
}
const name = this.getDefinitionName(definition);
return (!name ||
definition.kind === Kind.DIRECTIVE_DEFINITION ||
declaredNames.has(name));
});
return { ...subgraphDocument, definitions };
}
getDefinitionName(definition) {
return 'name' in definition ? definition.name?.value : undefined;
}
}