From a198c6a9224b715d68bb8de9ae258a57af751710 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Wed, 5 Oct 2022 09:31:20 -0500 Subject: [PATCH 1/3] refactor: decouple schema generation from driver start --- .../lib/drivers/apollo-federation.driver.ts | 14 +++++----- .../lib/drivers/apollo-gateway.driver.ts | 7 +++++ packages/apollo/lib/drivers/apollo.driver.ts | 13 +++------ .../lib/drivers/abstract-graphql.driver.ts | 5 ++++ .../federation/graphql-federation.factory.ts | 16 +++-------- packages/graphql/lib/graphql.factory.ts | 27 +++---------------- packages/graphql/lib/graphql.module.ts | 13 ++++++++- .../drivers/mercurius-federation.driver.ts | 19 ++++++++----- .../lib/drivers/mercurius-gateway.driver.ts | 13 ++++++++- .../mercurius/lib/drivers/mercurius.driver.ts | 5 +--- 10 files changed, 69 insertions(+), 63 deletions(-) diff --git a/packages/apollo/lib/drivers/apollo-federation.driver.ts b/packages/apollo/lib/drivers/apollo-federation.driver.ts index 6f39318c9..86f8b0abf 100644 --- a/packages/apollo/lib/drivers/apollo-federation.driver.ts +++ b/packages/apollo/lib/drivers/apollo-federation.driver.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { loadPackage } from '@nestjs/common/utils/load-package.util'; import { ModulesContainer } from '@nestjs/core'; import { extend, GraphQLFederationFactory } from '@nestjs/graphql'; +import { GraphQLSchema } from 'graphql'; import { ApolloDriverConfig } from '../interfaces'; import { PluginsExplorerService } from '../services/plugins-explorer.service'; import { ApolloBaseDriver } from './apollo-base.driver'; @@ -18,16 +19,17 @@ export class ApolloFederationDriver extends ApolloBaseDriver { this.pluginsExplorerService = new PluginsExplorerService(modulesContainer); } + async generateSchema(options: ApolloDriverConfig): Promise { + return await this.graphqlFederationFactory.mergeWithSchema(options); + } + public async start(options: ApolloDriverConfig): Promise { options.plugins = extend( options.plugins || [], this.pluginsExplorerService.explore(options), ); - const adapterOptions = await this.graphqlFederationFactory.mergeWithSchema( - options, - ); - await this.runExecutorFactoryIfPresent(adapterOptions); + await this.runExecutorFactoryIfPresent(options); if (options.definitions && options.definitions.path) { const { printSubgraphSchema } = loadPackage( @@ -36,12 +38,12 @@ export class ApolloFederationDriver extends ApolloBaseDriver { () => require('@apollo/subgraph'), ); await this.graphQlFactory.generateDefinitions( - printSubgraphSchema(adapterOptions.schema), + printSubgraphSchema(options.schema), options, ); } - await super.start(adapterOptions); + await super.start(options); if (options.installSubscriptionHandlers || options.subscriptions) { // TL;DR diff --git a/packages/apollo/lib/drivers/apollo-gateway.driver.ts b/packages/apollo/lib/drivers/apollo-gateway.driver.ts index d7e1ba627..6a9ce8bfc 100644 --- a/packages/apollo/lib/drivers/apollo-gateway.driver.ts +++ b/packages/apollo/lib/drivers/apollo-gateway.driver.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { loadPackage } from '@nestjs/common/utils/load-package.util'; import { ModulesContainer } from '@nestjs/core'; import { extend } from '@nestjs/graphql'; +import { GraphQLSchema } from 'graphql'; import { ApolloGatewayDriverConfig } from '../interfaces'; import { PluginsExplorerService } from '../services/plugins-explorer.service'; import { ApolloBaseDriver } from './apollo-base.driver'; @@ -15,6 +16,12 @@ export class ApolloGatewayDriver extends ApolloBaseDriver { + return new GraphQLSchema({}); + } + public async start(options: ApolloGatewayDriverConfig): Promise { options.server.plugins = extend( options.server.plugins || [], diff --git a/packages/apollo/lib/drivers/apollo.driver.ts b/packages/apollo/lib/drivers/apollo.driver.ts index 3aee010c5..2a7d43733 100644 --- a/packages/apollo/lib/drivers/apollo.driver.ts +++ b/packages/apollo/lib/drivers/apollo.driver.ts @@ -20,17 +20,12 @@ export class ApolloDriver extends ApolloBaseDriver { this.pluginsExplorerService = new PluginsExplorerService(modulesContainer); } - public async start(apolloOptions: ApolloDriverConfig) { - apolloOptions.plugins = extend( - apolloOptions.plugins || [], - this.pluginsExplorerService.explore(apolloOptions), + public async start(options: ApolloDriverConfig) { + options.plugins = extend( + options.plugins || [], + this.pluginsExplorerService.explore(options), ); - const options = - await this.graphQlFactory.mergeWithSchema( - apolloOptions, - ); - if (options.definitions && options.definitions.path) { await this.graphQlFactory.generateDefinitions( printSchema(options.schema), diff --git a/packages/graphql/lib/drivers/abstract-graphql.driver.ts b/packages/graphql/lib/drivers/abstract-graphql.driver.ts index 036fe578e..12640e680 100644 --- a/packages/graphql/lib/drivers/abstract-graphql.driver.ts +++ b/packages/graphql/lib/drivers/abstract-graphql.driver.ts @@ -1,5 +1,6 @@ import { Inject } from '@nestjs/common'; import { ApplicationConfig, HttpAdapterHost } from '@nestjs/core'; +import { GraphQLSchema } from 'graphql'; import { GraphQLFactory } from '../graphql.factory'; import { GqlModuleOptions, GraphQLDriver } from '../interfaces'; import { normalizeRoutePath } from '../utils'; @@ -36,6 +37,10 @@ export abstract class AbstractGraphQLDriver< return clonedOptions; } + public async generateSchema(options: TOptions): Promise { + return await this.graphQlFactory.mergeWithSchema(options); + } + public subscriptionWithFilter( instanceRef: unknown, filterFn: ( diff --git a/packages/graphql/lib/federation/graphql-federation.factory.ts b/packages/graphql/lib/federation/graphql-federation.factory.ts index ff4c856ee..0e15fc9b3 100644 --- a/packages/graphql/lib/federation/graphql-federation.factory.ts +++ b/packages/graphql/lib/federation/graphql-federation.factory.ts @@ -25,7 +25,6 @@ import { import { gql } from 'graphql-tag'; import { forEach, isEmpty } from 'lodash'; import { GraphQLSchemaBuilder } from '../graphql-schema.builder'; -import { GraphQLSchemaHost } from '../graphql-schema.host'; import { AutoSchemaFileValue, BuildFederatedSchemaOptions, @@ -46,7 +45,6 @@ export class GraphQLFederationFactory { private readonly resolversExplorerService: ResolversExplorerService, private readonly scalarsExplorerService: ScalarsExplorerService, private readonly gqlSchemaBuilder: GraphQLSchemaBuilder, - private readonly gqlSchemaHost: GraphQLSchemaHost, private readonly typeDefsDecoratorFactory: TypeDefsDecoratorFactory, ) {} @@ -55,9 +53,9 @@ export class GraphQLFederationFactory { buildFederatedSchema?: ( options: BuildFederatedSchemaOptions, ) => GraphQLSchema, - ): Promise { - const transformSchema = async (schema: GraphQLSchema) => - options.transformSchema ? options.transformSchema(schema) : schema; + ): Promise { + const transformSchema = + options.transformSchema ?? ((schema: GraphQLSchema) => schema); let schema: GraphQLSchema; if (options.autoSchemaFile) { @@ -68,13 +66,7 @@ export class GraphQLFederationFactory { schema = this.buildSchemaFromTypeDefs(options); } - this.gqlSchemaHost.schema = schema; - - return { - ...options, - schema: await transformSchema(schema), - typeDefs: undefined, - }; + return await transformSchema(schema); } private buildSchemaFromTypeDefs(options: T) { diff --git a/packages/graphql/lib/graphql.factory.ts b/packages/graphql/lib/graphql.factory.ts index 58592062b..bec039f6f 100644 --- a/packages/graphql/lib/graphql.factory.ts +++ b/packages/graphql/lib/graphql.factory.ts @@ -16,7 +16,6 @@ import { GraphQLAstExplorer, } from './graphql-ast.explorer'; import { GraphQLSchemaBuilder } from './graphql-schema.builder'; -import { GraphQLSchemaHost } from './graphql-schema.host'; import { GqlModuleOptions } from './interfaces'; import { ResolversExplorerService, ScalarsExplorerService } from './services'; import { extend, removeTempField } from './utils'; @@ -28,12 +27,11 @@ export class GraphQLFactory { private readonly scalarsExplorerService: ScalarsExplorerService, private readonly graphqlAstExplorer: GraphQLAstExplorer, private readonly gqlSchemaBuilder: GraphQLSchemaBuilder, - private readonly gqlSchemaHost: GraphQLSchemaHost, ) {} async mergeWithSchema( options: T = { typeDefs: [] } as T, - ): Promise { + ): Promise { const resolvers = this.resolversExplorerService.explore(); const typesResolvers = extend( this.scalarsExplorerService.explore(), @@ -76,22 +74,11 @@ export class GraphQLFactory { schema = new GraphQLSchema(schemaConfig); schema = await transformSchema(schema); schema = options.sortSchema ? lexicographicSortSchema(schema) : schema; - this.gqlSchemaHost.schema = schema; - - return { - ...options, - typeDefs: undefined, - schema, - }; + return schema; } if (isEmpty(options.typeDefs)) { const schema = await transformSchema(options.schema); - this.gqlSchemaHost.schema = schema; - return { - ...options, - typeDefs: undefined, - schema, - }; + return schema; } const executableSchema = makeExecutableSchema({ resolvers: extend(typesResolvers, options.resolvers), @@ -110,13 +97,7 @@ export class GraphQLFactory { removeTempField(schema); schema = await transformSchema(schema); schema = options.sortSchema ? lexicographicSortSchema(schema) : schema; - this.gqlSchemaHost.schema = schema; - - return { - ...options, - typeDefs: undefined, - schema, - }; + return schema; } overrideOrExtendResolvers( diff --git a/packages/graphql/lib/graphql.module.ts b/packages/graphql/lib/graphql.module.ts index b7705d164..7b64c9317 100644 --- a/packages/graphql/lib/graphql.module.ts +++ b/packages/graphql/lib/graphql.module.ts @@ -64,6 +64,7 @@ export class GraphQLModule< @Inject(GRAPHQL_MODULE_OPTIONS) private readonly options: GqlModuleOptions, private readonly _graphQlAdapter: AbstractGraphQLDriver, private readonly graphQlTypesLoader: GraphQLTypesLoader, + private readonly gqlSchemaHost: GraphQLSchemaHost, ) {} async onModuleDestroy() { @@ -158,10 +159,20 @@ export class GraphQLModule< (await this.graphQlTypesLoader.mergeTypesByPaths(typePaths)) || []; const mergedTypeDefs = extend(typeDefs, options.typeDefs); - await this._graphQlAdapter.start({ + + const gqlSchema = await this._graphQlAdapter.generateSchema({ ...options, typeDefs: mergedTypeDefs, }); + this.gqlSchemaHost.schema = gqlSchema; + + const completeOptions = { + ...options, + schema: gqlSchema, + typeDefs: undefined, + }; + + await this._graphQlAdapter.start(completeOptions); if (options.path) { GraphQLModule.logger.log( diff --git a/packages/mercurius/lib/drivers/mercurius-federation.driver.ts b/packages/mercurius/lib/drivers/mercurius-federation.driver.ts index 1a84e3322..41e74d89b 100644 --- a/packages/mercurius/lib/drivers/mercurius-federation.driver.ts +++ b/packages/mercurius/lib/drivers/mercurius-federation.driver.ts @@ -4,7 +4,7 @@ import { GraphQLFederationFactory, } from '@nestjs/graphql'; import { FastifyInstance, FastifyLoggerInstance } from 'fastify'; -import { printSchema } from 'graphql'; +import { GraphQLSchema, printSchema } from 'graphql'; import { IncomingMessage, Server, ServerResponse } from 'http'; import mercurius from 'mercurius'; import { MercuriusDriverConfig } from '../interfaces/mercurius-driver-config.interface'; @@ -28,12 +28,17 @@ export class MercuriusFederationDriver extends AbstractGraphQLDriver { + return await this.graphqlFederationFactory.mergeWithSchema( + options, + buildMercuriusFederatedSchema, + ); + } + public async start(options: MercuriusDriverConfig) { - const { plugins, ...adapterOptions } = - await this.graphqlFederationFactory.mergeWithSchema( - options, - buildMercuriusFederatedSchema, - ); + const { plugins, ...adapterOptions } = options; if (adapterOptions.definitions && adapterOptions.definitions.path) { await this.graphQlFactory.generateDefinitions( @@ -55,6 +60,6 @@ export class MercuriusFederationDriver extends AbstractGraphQLDriver {} } diff --git a/packages/mercurius/lib/drivers/mercurius-gateway.driver.ts b/packages/mercurius/lib/drivers/mercurius-gateway.driver.ts index 9417da906..18a9b2072 100644 --- a/packages/mercurius/lib/drivers/mercurius-gateway.driver.ts +++ b/packages/mercurius/lib/drivers/mercurius-gateway.driver.ts @@ -1,5 +1,6 @@ import { AbstractGraphQLDriver } from '@nestjs/graphql'; import { FastifyInstance, FastifyLoggerInstance } from 'fastify'; +import { GraphQLSchema } from 'graphql'; import { IncomingMessage, Server, ServerResponse } from 'http'; import mercurius from 'mercurius'; import { MercuriusDriverConfig } from '../interfaces/mercurius-driver-config.interface'; @@ -15,6 +16,12 @@ export class MercuriusGatewayDriver extends AbstractGraphQLDriver { + return new GraphQLSchema({}); + } + public async start(options: MercuriusDriverConfig) { const httpAdapter = this.httpAdapterHost.httpAdapter; const platformName = httpAdapter.getType(); @@ -23,7 +30,11 @@ export class MercuriusGatewayDriver extends AbstractGraphQLDriver(); await app.register(mercurius, { ...mercuriusOptions, diff --git a/packages/mercurius/lib/drivers/mercurius.driver.ts b/packages/mercurius/lib/drivers/mercurius.driver.ts index 712d28695..232075f62 100644 --- a/packages/mercurius/lib/drivers/mercurius.driver.ts +++ b/packages/mercurius/lib/drivers/mercurius.driver.ts @@ -18,10 +18,7 @@ export class MercuriusDriver extends AbstractGraphQLDriver( - mercuriusOptions, - ); + const { plugins, ...options } = mercuriusOptions; if (options.definitions && options.definitions.path) { await this.graphQlFactory.generateDefinitions( From e30f0858a26b5fdedda52319a5f4e28a88820970 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Wed, 5 Oct 2022 09:37:11 -0500 Subject: [PATCH 2/3] refactor: always generate schema regardless of http adapter existence --- .../graphql/lib/drivers/abstract-graphql.driver.ts | 4 ++-- packages/graphql/lib/graphql.module.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/graphql/lib/drivers/abstract-graphql.driver.ts b/packages/graphql/lib/drivers/abstract-graphql.driver.ts index 12640e680..3abb73a33 100644 --- a/packages/graphql/lib/drivers/abstract-graphql.driver.ts +++ b/packages/graphql/lib/drivers/abstract-graphql.driver.ts @@ -13,7 +13,7 @@ export abstract class AbstractGraphQLDriver< protected readonly httpAdapterHost: HttpAdapterHost; @Inject() - protected readonly applicationConfig: ApplicationConfig; + protected readonly applicationConfig?: ApplicationConfig; @Inject() protected readonly graphQlFactory: GraphQLFactory; @@ -54,7 +54,7 @@ export abstract class AbstractGraphQLDriver< } protected getNormalizedPath(options: TOptions): string { - const prefix = this.applicationConfig.getGlobalPrefix(); + const prefix = this.applicationConfig?.getGlobalPrefix() ?? ''; const useGlobalPrefix = prefix && options.useGlobalPrefix; const gqlOptionsPath = normalizeRoutePath(options.path); return useGlobalPrefix diff --git a/packages/graphql/lib/graphql.module.ts b/packages/graphql/lib/graphql.module.ts index 7b64c9317..0c01bafd4 100644 --- a/packages/graphql/lib/graphql.module.ts +++ b/packages/graphql/lib/graphql.module.ts @@ -146,11 +146,6 @@ export class GraphQLModule< } async onModuleInit() { - const httpAdapter = this.httpAdapterHost?.httpAdapter; - if (!httpAdapter) { - return; - } - const options = await this._graphQlAdapter.mergeDefaultOptions( this.options, ); @@ -172,6 +167,11 @@ export class GraphQLModule< typeDefs: undefined, }; + const httpAdapter = this.httpAdapterHost?.httpAdapter; + if (!httpAdapter) { + return; + } + await this._graphQlAdapter.start(completeOptions); if (options.path) { From 6408c1facd44610f5634ef6757d1007e0cc49821 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Wed, 5 Oct 2022 09:53:33 -0500 Subject: [PATCH 3/3] refactor: rename `mergeWithSchema` to `generateSchema` This better matches the method signature. --- packages/apollo/lib/drivers/apollo-federation.driver.ts | 2 +- packages/graphql/lib/drivers/abstract-graphql.driver.ts | 2 +- .../graphql/lib/federation/graphql-federation.factory.ts | 9 ++++++--- packages/graphql/lib/graphql.factory.ts | 4 ++-- .../mercurius/lib/drivers/mercurius-federation.driver.ts | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/apollo/lib/drivers/apollo-federation.driver.ts b/packages/apollo/lib/drivers/apollo-federation.driver.ts index 86f8b0abf..cf6631d89 100644 --- a/packages/apollo/lib/drivers/apollo-federation.driver.ts +++ b/packages/apollo/lib/drivers/apollo-federation.driver.ts @@ -20,7 +20,7 @@ export class ApolloFederationDriver extends ApolloBaseDriver { } async generateSchema(options: ApolloDriverConfig): Promise { - return await this.graphqlFederationFactory.mergeWithSchema(options); + return await this.graphqlFederationFactory.generateSchema(options); } public async start(options: ApolloDriverConfig): Promise { diff --git a/packages/graphql/lib/drivers/abstract-graphql.driver.ts b/packages/graphql/lib/drivers/abstract-graphql.driver.ts index 3abb73a33..72e2944e4 100644 --- a/packages/graphql/lib/drivers/abstract-graphql.driver.ts +++ b/packages/graphql/lib/drivers/abstract-graphql.driver.ts @@ -38,7 +38,7 @@ export abstract class AbstractGraphQLDriver< } public async generateSchema(options: TOptions): Promise { - return await this.graphQlFactory.mergeWithSchema(options); + return await this.graphQlFactory.generateSchema(options); } public subscriptionWithFilter( diff --git a/packages/graphql/lib/federation/graphql-federation.factory.ts b/packages/graphql/lib/federation/graphql-federation.factory.ts index 0e15fc9b3..644aacd04 100644 --- a/packages/graphql/lib/federation/graphql-federation.factory.ts +++ b/packages/graphql/lib/federation/graphql-federation.factory.ts @@ -48,7 +48,7 @@ export class GraphQLFederationFactory { private readonly typeDefsDecoratorFactory: TypeDefsDecoratorFactory, ) {} - async mergeWithSchema( + async generateSchema( options: T = {} as T, buildFederatedSchema?: ( options: BuildFederatedSchemaOptions, @@ -59,7 +59,10 @@ export class GraphQLFederationFactory { let schema: GraphQLSchema; if (options.autoSchemaFile) { - schema = await this.generateSchema(options, buildFederatedSchema); + schema = await this.generateSchemaFromCodeFirst( + options, + buildFederatedSchema, + ); } else if (isEmpty(options.typeDefs)) { schema = options.schema; } else { @@ -85,7 +88,7 @@ export class GraphQLFederationFactory { ]); } - private async generateSchema( + private async generateSchemaFromCodeFirst( options: T, buildFederatedSchema?: ( options: BuildFederatedSchemaOptions, diff --git a/packages/graphql/lib/graphql.factory.ts b/packages/graphql/lib/graphql.factory.ts index bec039f6f..ca3ebd10b 100644 --- a/packages/graphql/lib/graphql.factory.ts +++ b/packages/graphql/lib/graphql.factory.ts @@ -29,7 +29,7 @@ export class GraphQLFactory { private readonly gqlSchemaBuilder: GraphQLSchemaBuilder, ) {} - async mergeWithSchema( + async generateSchema( options: T = { typeDefs: [] } as T, ): Promise { const resolvers = this.resolversExplorerService.explore(); @@ -100,7 +100,7 @@ export class GraphQLFactory { return schema; } - overrideOrExtendResolvers( + private overrideOrExtendResolvers( executableSchemaConfig: GraphQLSchemaConfig, autoGeneratedSchemaConfig: GraphQLSchemaConfig, ): GraphQLSchemaConfig { diff --git a/packages/mercurius/lib/drivers/mercurius-federation.driver.ts b/packages/mercurius/lib/drivers/mercurius-federation.driver.ts index 41e74d89b..20bd485d0 100644 --- a/packages/mercurius/lib/drivers/mercurius-federation.driver.ts +++ b/packages/mercurius/lib/drivers/mercurius-federation.driver.ts @@ -31,7 +31,7 @@ export class MercuriusFederationDriver extends AbstractGraphQLDriver { - return await this.graphqlFederationFactory.mergeWithSchema( + return await this.graphqlFederationFactory.generateSchema( options, buildMercuriusFederatedSchema, );