Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor schema generation to be independent of driver start #2435

Merged
merged 3 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions packages/apollo/lib/drivers/apollo-federation.driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -18,16 +19,17 @@ export class ApolloFederationDriver extends ApolloBaseDriver {
this.pluginsExplorerService = new PluginsExplorerService(modulesContainer);
}

async generateSchema(options: ApolloDriverConfig): Promise<GraphQLSchema> {
return await this.graphqlFederationFactory.generateSchema(options);
}

public async start(options: ApolloDriverConfig): Promise<void> {
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(
Expand All @@ -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 <https://github.com/apollographql/apollo-server/issues/2776>
Expand Down
7 changes: 7 additions & 0 deletions packages/apollo/lib/drivers/apollo-gateway.driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -15,6 +16,12 @@ export class ApolloGatewayDriver extends ApolloBaseDriver<ApolloGatewayDriverCon
this.pluginsExplorerService = new PluginsExplorerService(modulesContainer);
}

public async generateSchema(
options: ApolloGatewayDriverConfig,
): Promise<GraphQLSchema> {
return new GraphQLSchema({});
}

public async start(options: ApolloGatewayDriverConfig): Promise<void> {
options.server.plugins = extend(
options.server.plugins || [],
Expand Down
13 changes: 4 additions & 9 deletions packages/apollo/lib/drivers/apollo.driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ApolloDriverConfig>(
apolloOptions,
);

if (options.definitions && options.definitions.path) {
await this.graphQlFactory.generateDefinitions(
printSchema(options.schema),
Expand Down
9 changes: 7 additions & 2 deletions packages/graphql/lib/drivers/abstract-graphql.driver.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -12,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;
Expand All @@ -36,6 +37,10 @@ export abstract class AbstractGraphQLDriver<
return clonedOptions;
}

public async generateSchema(options: TOptions): Promise<GraphQLSchema> {
return await this.graphQlFactory.generateSchema(options);
}

public subscriptionWithFilter(
instanceRef: unknown,
filterFn: (
Expand All @@ -49,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
Expand Down
25 changes: 10 additions & 15 deletions packages/graphql/lib/federation/graphql-federation.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -46,35 +45,31 @@ export class GraphQLFederationFactory {
private readonly resolversExplorerService: ResolversExplorerService,
private readonly scalarsExplorerService: ScalarsExplorerService,
private readonly gqlSchemaBuilder: GraphQLSchemaBuilder,
private readonly gqlSchemaHost: GraphQLSchemaHost,
private readonly typeDefsDecoratorFactory: TypeDefsDecoratorFactory,
) {}

async mergeWithSchema<T extends GqlModuleOptions>(
async generateSchema<T extends GqlModuleOptions>(
options: T = {} as T,
buildFederatedSchema?: (
options: BuildFederatedSchemaOptions,
) => GraphQLSchema,
): Promise<T> {
const transformSchema = async (schema: GraphQLSchema) =>
options.transformSchema ? options.transformSchema(schema) : schema;
): Promise<GraphQLSchema> {
const transformSchema =
options.transformSchema ?? ((schema: GraphQLSchema) => schema);

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 {
schema = this.buildSchemaFromTypeDefs(options);
}

this.gqlSchemaHost.schema = schema;

return {
...options,
schema: await transformSchema(schema),
typeDefs: undefined,
};
return await transformSchema(schema);
}

private buildSchemaFromTypeDefs<T extends GqlModuleOptions>(options: T) {
Expand All @@ -93,7 +88,7 @@ export class GraphQLFederationFactory {
]);
}

private async generateSchema<T extends GqlModuleOptions>(
private async generateSchemaFromCodeFirst<T extends GqlModuleOptions>(
options: T,
buildFederatedSchema?: (
options: BuildFederatedSchemaOptions,
Expand Down
31 changes: 6 additions & 25 deletions packages/graphql/lib/graphql.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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<T extends GqlModuleOptions>(
async generateSchema<T extends GqlModuleOptions>(
options: T = { typeDefs: [] } as T,
): Promise<T> {
): Promise<GraphQLSchema> {
const resolvers = this.resolversExplorerService.explore();
const typesResolvers = extend(
this.scalarsExplorerService.explore(),
Expand Down Expand Up @@ -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),
Expand All @@ -110,16 +97,10 @@ 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(
private overrideOrExtendResolvers(
executableSchemaConfig: GraphQLSchemaConfig,
autoGeneratedSchemaConfig: GraphQLSchemaConfig,
): GraphQLSchemaConfig {
Expand Down
23 changes: 17 additions & 6 deletions packages/graphql/lib/graphql.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -145,11 +146,6 @@ export class GraphQLModule<
}

async onModuleInit() {
const httpAdapter = this.httpAdapterHost?.httpAdapter;
if (!httpAdapter) {
return;
}

const options = await this._graphQlAdapter.mergeDefaultOptions(
this.options,
);
Expand All @@ -158,10 +154,25 @@ 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,
};

const httpAdapter = this.httpAdapterHost?.httpAdapter;
if (!httpAdapter) {
return;
}

await this._graphQlAdapter.start(completeOptions);

if (options.path) {
GraphQLModule.logger.log(
Expand Down
19 changes: 12 additions & 7 deletions packages/mercurius/lib/drivers/mercurius-federation.driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -28,12 +28,17 @@ export class MercuriusFederationDriver extends AbstractGraphQLDriver<MercuriusDr
return this.httpAdapterHost?.httpAdapter?.getInstance?.();
}

public override async generateSchema(
options: MercuriusDriverConfig,
): Promise<GraphQLSchema> {
return await this.graphqlFederationFactory.generateSchema(
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(
Expand All @@ -55,6 +60,6 @@ export class MercuriusFederationDriver extends AbstractGraphQLDriver<MercuriusDr
await registerMercuriusPlugin(app, plugins);
}

/* eslit-disable-next-line @typescript-eslint/no-empty-function */
/* eslint-disable-next-line @typescript-eslint/no-empty-function */
public async stop(): Promise<void> {}
}
13 changes: 12 additions & 1 deletion packages/mercurius/lib/drivers/mercurius-gateway.driver.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -15,6 +16,12 @@ export class MercuriusGatewayDriver extends AbstractGraphQLDriver<MercuriusDrive
return this.httpAdapterHost?.httpAdapter?.getInstance?.();
}

public async generateSchema(
options: MercuriusDriverConfig,
): Promise<GraphQLSchema> {
return new GraphQLSchema({});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since gateways don't generate a schema, I stubbed it here & omitted below. Same for apollo gateway.

If this feels hacky, I could change return type to be nullable.

}

public async start(options: MercuriusDriverConfig) {
const httpAdapter = this.httpAdapterHost.httpAdapter;
const platformName = httpAdapter.getType();
Expand All @@ -23,7 +30,11 @@ export class MercuriusGatewayDriver extends AbstractGraphQLDriver<MercuriusDrive
throw new Error(`No support for current HttpAdapter: ${platformName}`);
}

const { plugins, ...mercuriusOptions } = options;
const {
plugins,
schema: _, // Schema stubbed to be compatible with other drivers, ignore.
...mercuriusOptions
} = options;
const app = httpAdapter.getInstance<FastifyInstance>();
await app.register(mercurius, {
...mercuriusOptions,
Expand Down
5 changes: 1 addition & 4 deletions packages/mercurius/lib/drivers/mercurius.driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ export class MercuriusDriver extends AbstractGraphQLDriver<MercuriusDriverConfig
}

public async start(mercuriusOptions: MercuriusDriverConfig) {
const { plugins, ...options } =
await this.graphQlFactory.mergeWithSchema<MercuriusDriverConfig>(
mercuriusOptions,
);
const { plugins, ...options } = mercuriusOptions;

if (options.definitions && options.definitions.path) {
await this.graphQlFactory.generateDefinitions(
Expand Down