From 820282d38b1f2e28d3e838b7ea43bd4a59837e2f Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Tue, 8 Sep 2020 13:54:47 -0700 Subject: [PATCH 1/3] feat(graphql): add support for graphql subscriptions Signed-off-by: Raymond Feng --- extensions/graphql/src/decorators/index.ts | 4 ++++ extensions/graphql/src/graphql.server.ts | 14 +++++++++++++- extensions/graphql/src/keys.ts | 9 ++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/extensions/graphql/src/decorators/index.ts b/extensions/graphql/src/decorators/index.ts index cd0762e4d546..b345e5f2c025 100644 --- a/extensions/graphql/src/decorators/index.ts +++ b/extensions/graphql/src/decorators/index.ts @@ -15,9 +15,11 @@ import { InputType, Mutation, ObjectType, + PubSub, Query, Resolver, Root, + Subscription, } from 'type-graphql'; /** @@ -38,3 +40,5 @@ export const field = Field; export const inputType = InputType; export const objectType = ObjectType; export const authorized = Authorized; +export const subscription = Subscription; +export const pubSub = PubSub; diff --git a/extensions/graphql/src/graphql.server.ts b/extensions/graphql/src/graphql.server.ts index b0edf1d59041..162bf3fa2cdd 100644 --- a/extensions/graphql/src/graphql.server.ts +++ b/extensions/graphql/src/graphql.server.ts @@ -20,7 +20,12 @@ import { } from '@loopback/core'; import {HttpOptions, HttpServer} from '@loopback/http-server'; import {ContextFunction} from 'apollo-server-core'; -import {ApolloServer, ApolloServerExpressConfig} from 'apollo-server-express'; +import { + ApolloServer, + ApolloServerExpressConfig, + PubSub, + PubSubEngine, +} from 'apollo-server-express'; import {ExpressContext} from 'apollo-server-express/dist/ApolloServer'; import express from 'express'; import { @@ -137,6 +142,11 @@ export class GraphQLServer extends Context implements Server { optional: true, })) ?? ((resolverData, roles) => true); + const pubSub: PubSubEngine | undefined = + (await this.get(GraphQLBindings.PUB_SUB_ENGINE, { + optional: true, + })) ?? new PubSub(); + // build TypeGraphQL executable schema const schema = await buildSchema({ // See https://github.com/MichalLytek/type-graphql/issues/150#issuecomment-420181526 @@ -146,6 +156,7 @@ export class GraphQLServer extends Context implements Server { // emitSchemaFile: path.resolve(__dirname, 'schema.gql'), container: new LoopBackContainer(this), authChecker, + pubSub, globalMiddlewares: await this.getMiddleware(), }); @@ -159,6 +170,7 @@ export class GraphQLServer extends Context implements Server { // enable GraphQL Playground playground: true, context: graphqlContextResolver, + subscriptions: {}, ...this.options.graphql, schema, }; diff --git a/extensions/graphql/src/keys.ts b/extensions/graphql/src/keys.ts index 82c8d8367630..75f8fe8074e1 100644 --- a/extensions/graphql/src/keys.ts +++ b/extensions/graphql/src/keys.ts @@ -4,7 +4,7 @@ // License text available at https://opensource.org/licenses/MIT import {BindingKey, Constructor} from '@loopback/core'; -import {AuthChecker, ResolverData} from 'type-graphql'; +import {AuthChecker, PubSubEngine, ResolverData} from 'type-graphql'; import {GraphQLComponent} from './graphql.component'; import {ContextFunction, ExpressContext, GraphQLServer} from './graphql.server'; @@ -40,6 +40,13 @@ export namespace GraphQLBindings { 'graphql.authChecker', ); + /** + * Binding key for the GraphQL pub/sub engine + */ + export const PUB_SUB_ENGINE = BindingKey.create( + 'graphql.pubSubEngine', + ); + /** * Binding key for the GraphQL resolver data - which is bound per request */ From 9722e3dbefb5444f98483f1dd2b066ab1bb39cf4 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Tue, 8 Sep 2020 13:55:53 -0700 Subject: [PATCH 2/3] feat(example-graphql): add subscriptions to the graphql example Signed-off-by: Raymond Feng --- .../src/__tests__/acceptance/graphql-tests.ts | 11 ++++++++++- .../__tests__/acceptance/graphql.acceptance.ts | 6 +++++- examples/graphql/src/application.ts | 1 + .../src/graphql-resolvers/recipe-resolver.ts | 18 ++++++++++++++++-- examples/graphql/src/index.ts | 8 +++++++- 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/examples/graphql/src/__tests__/acceptance/graphql-tests.ts b/examples/graphql/src/__tests__/acceptance/graphql-tests.ts index e82b3919a9e9..47ea205cbc73 100644 --- a/examples/graphql/src/__tests__/acceptance/graphql-tests.ts +++ b/examples/graphql/src/__tests__/acceptance/graphql-tests.ts @@ -125,4 +125,13 @@ mutation AddRecipe { numberInCollection creationDate } -}`; +} + +subscription AllNotifications { + recipeCreated { + id + numberInCollection + creationDate + } +} +`; diff --git a/examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts b/examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts index b48247e63f63..07b27e26117c 100644 --- a/examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts +++ b/examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts @@ -28,7 +28,11 @@ describe('GraphQL server', () => { runTests(() => supertest(server.httpServer?.url)); async function givenServer() { - server = new GraphQLServer({host: '127.0.0.1', port: 0}); + server = new GraphQLServer({ + host: '127.0.0.1', + port: 0, + graphql: {subscriptions: '/subscriptions'}, + }); server.resolver(RecipeResolver); server.bind('recipes').to([...sampleRecipes]); diff --git a/examples/graphql/src/application.ts b/examples/graphql/src/application.ts index 90a1da6ed5fc..2bf332f10711 100644 --- a/examples/graphql/src/application.ts +++ b/examples/graphql/src/application.ts @@ -23,6 +23,7 @@ export class GraphqlDemoApplication extends BootMixin( const server = this.getSync(GraphQLBindings.GRAPHQL_SERVER); this.expressMiddleware('middleware.express.GraphQL', server.expressApp); this.configure(GraphQLBindings.GRAPHQL_SERVER).to({ + ...this.options.graphql, asMiddlewareOnly: true, }); diff --git a/examples/graphql/src/graphql-resolvers/recipe-resolver.ts b/examples/graphql/src/graphql-resolvers/recipe-resolver.ts index b3d9bb9a305f..6bb771f0ce72 100644 --- a/examples/graphql/src/graphql-resolvers/recipe-resolver.ts +++ b/examples/graphql/src/graphql-resolvers/recipe-resolver.ts @@ -11,11 +11,14 @@ import { GraphQLBindings, Int, mutation, + Publisher, + pubSub, query, resolver, ResolverData, ResolverInterface, root, + subscription, } from '@loopback/graphql'; import {repository} from '@loopback/repository'; import {RecipeInput} from '../graphql-types/recipe-input'; @@ -46,8 +49,19 @@ export class RecipeResolver implements ResolverInterface { } @mutation(returns => Recipe) - async addRecipe(@arg('recipe') recipe: RecipeInput): Promise { - return this.recipeRepo.add(recipe); + async addRecipe( + @arg('recipe') recipe: RecipeInput, + @pubSub('recipeCreated') publisher: Publisher, + ): Promise { + const result = await this.recipeRepo.add(recipe); + await publisher(result); + return result; + } + + @subscription(returns => Recipe, {topics: 'recipeCreated'}) + async recipeCreated(@root() recipe: Recipe) { + console.log('recipe created', recipe); + return recipe; } @fieldResolver() diff --git a/examples/graphql/src/index.ts b/examples/graphql/src/index.ts index 06d5e9a4f49e..1ea440a047bc 100644 --- a/examples/graphql/src/index.ts +++ b/examples/graphql/src/index.ts @@ -1,8 +1,8 @@ +import {GraphQLServerOptions} from '@loopback/graphql'; // Copyright IBM Corp. 2020. All Rights Reserved. // Node module: @loopback/example-graphql // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT - import {ApplicationConfig, GraphqlDemoApplication} from './application'; export * from './application'; @@ -20,6 +20,11 @@ export async function main(options: ApplicationConfig = {}) { } if (require.main === module) { + const graphqlCfg: GraphQLServerOptions = { + graphql: { + subscriptions: '/subscriptions', + }, + }; // Run the application const config = { rest: { @@ -36,6 +41,7 @@ if (require.main === module) { setServersFromRequest: true, }, }, + graphql: graphqlCfg, }; main(config).catch(err => { console.error('Cannot start the application.', err); From d92fbbd25d7a5e29ebd4eae8b355e85070fabe5a Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Wed, 9 Sep 2020 10:58:24 -0700 Subject: [PATCH 3/3] feat(graphql): improve graphql server configuration Signed-off-by: Raymond Feng --- .../acceptance/graphql.acceptance.ts | 2 +- examples/graphql/src/application.ts | 4 -- .../src/graphql-resolvers/recipe-resolver.ts | 5 +- examples/graphql/src/index.ts | 3 +- extensions/graphql/README.md | 15 ++++- .../__tests__/unit/graphql.component.unit.ts | 30 ++++++++- .../src/__tests__/unit/graphql.server.unit.ts | 23 ++++++- extensions/graphql/src/graphql.component.ts | 12 +++- extensions/graphql/src/graphql.server.ts | 62 ++++++++----------- extensions/graphql/src/keys.ts | 12 +++- extensions/graphql/src/types.ts | 25 ++++++++ 11 files changed, 137 insertions(+), 56 deletions(-) diff --git a/examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts b/examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts index 07b27e26117c..840b3bdc4f77 100644 --- a/examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts +++ b/examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts @@ -31,7 +31,7 @@ describe('GraphQL server', () => { server = new GraphQLServer({ host: '127.0.0.1', port: 0, - graphql: {subscriptions: '/subscriptions'}, + apollo: {subscriptions: '/subscriptions'}, }); server.resolver(RecipeResolver); diff --git a/examples/graphql/src/application.ts b/examples/graphql/src/application.ts index 2bf332f10711..cdef25bbf11a 100644 --- a/examples/graphql/src/application.ts +++ b/examples/graphql/src/application.ts @@ -22,10 +22,6 @@ export class GraphqlDemoApplication extends BootMixin( this.component(GraphQLComponent); const server = this.getSync(GraphQLBindings.GRAPHQL_SERVER); this.expressMiddleware('middleware.express.GraphQL', server.expressApp); - this.configure(GraphQLBindings.GRAPHQL_SERVER).to({ - ...this.options.graphql, - asMiddlewareOnly: true, - }); // It's possible to register a graphql context resolver this.bind(GraphQLBindings.GRAPHQL_CONTEXT_RESOLVER).to(context => { diff --git a/examples/graphql/src/graphql-resolvers/recipe-resolver.ts b/examples/graphql/src/graphql-resolvers/recipe-resolver.ts index 6bb771f0ce72..b267b5139336 100644 --- a/examples/graphql/src/graphql-resolvers/recipe-resolver.ts +++ b/examples/graphql/src/graphql-resolvers/recipe-resolver.ts @@ -51,16 +51,15 @@ export class RecipeResolver implements ResolverInterface { @mutation(returns => Recipe) async addRecipe( @arg('recipe') recipe: RecipeInput, - @pubSub('recipeCreated') publisher: Publisher, + @pubSub('recipeCreated') publish: Publisher, ): Promise { const result = await this.recipeRepo.add(recipe); - await publisher(result); + await publish(result); return result; } @subscription(returns => Recipe, {topics: 'recipeCreated'}) async recipeCreated(@root() recipe: Recipe) { - console.log('recipe created', recipe); return recipe; } diff --git a/examples/graphql/src/index.ts b/examples/graphql/src/index.ts index 1ea440a047bc..68a7ecb9d21f 100644 --- a/examples/graphql/src/index.ts +++ b/examples/graphql/src/index.ts @@ -21,9 +21,10 @@ export async function main(options: ApplicationConfig = {}) { if (require.main === module) { const graphqlCfg: GraphQLServerOptions = { - graphql: { + apollo: { subscriptions: '/subscriptions', }, + asMiddlewareOnly: true, }; // Run the application const config = { diff --git a/extensions/graphql/README.md b/extensions/graphql/README.md index 0e8b8afa6e23..8ee20d508650 100644 --- a/extensions/graphql/README.md +++ b/extensions/graphql/README.md @@ -68,11 +68,11 @@ export class GraphqlDemoApplication extends BootMixin( super(options); this.component(GraphQLComponent); - const server = this.getSync(GraphQLBindings.GRAPHQL_SERVER); - this.expressMiddleware('middleware.express.GraphQL', server.expressApp); this.configure(GraphQLBindings.GRAPHQL_SERVER).to({ asMiddlewareOnly: true, }); + const server = this.getSync(GraphQLBindings.GRAPHQL_SERVER); + this.expressMiddleware('middleware.express.GraphQL', server.expressApp); // ... @@ -89,6 +89,17 @@ export class GraphqlDemoApplication extends BootMixin( } ``` +The GraphQLServer configuration can also be passed in from the application +config, such as: + +```ts +const app = new Application({ + graphql: { + asMiddlewareOnly: true, + }, +}); +``` + ## Add GraphQL types The `@loopback/graphql` packages supports GraphQL schemas to be defined using diff --git a/extensions/graphql/src/__tests__/unit/graphql.component.unit.ts b/extensions/graphql/src/__tests__/unit/graphql.component.unit.ts index b9de6ce4f1a9..832cdb6c7667 100644 --- a/extensions/graphql/src/__tests__/unit/graphql.component.unit.ts +++ b/extensions/graphql/src/__tests__/unit/graphql.component.unit.ts @@ -5,14 +5,38 @@ import {Application} from '@loopback/core'; import {expect} from '@loopback/testlab'; -import {GraphQLComponent} from '../../graphql.component'; -import {GraphQLBindings} from '../../keys'; +import {GraphQLBindings, GraphQLComponent} from '../..'; describe('GraphQL component', () => { - it('binds server and booter bindings;', () => { + it('binds server and booter bindings', () => { const app = new Application(); app.component(GraphQLComponent); expect(app.isBound(GraphQLBindings.COMPONENT)).to.be.true(); expect(app.isBound('booters.GraphQLResolverBooter')).to.be.true(); }); + + it('configures GraphQL server from app config', () => { + const app = new Application({ + graphql: { + asMiddlewareOnly: true, + }, + }); + app.component(GraphQLComponent); + expect(app.getConfigSync(GraphQLBindings.GRAPHQL_SERVER)).to.eql({ + asMiddlewareOnly: true, + }); + }); + + it('configures GraphQL server to override app config', () => { + const app = new Application({ + graphql: { + asMiddlewareOnly: true, + }, + }); + app.component(GraphQLComponent); + app.configure(GraphQLBindings.GRAPHQL_SERVER).to({asMiddlewareOnly: false}); + expect(app.getConfigSync(GraphQLBindings.GRAPHQL_SERVER)).to.eql({ + asMiddlewareOnly: false, + }); + }); }); diff --git a/extensions/graphql/src/__tests__/unit/graphql.server.unit.ts b/extensions/graphql/src/__tests__/unit/graphql.server.unit.ts index ef5e1860ca1f..578e10d67157 100644 --- a/extensions/graphql/src/__tests__/unit/graphql.server.unit.ts +++ b/extensions/graphql/src/__tests__/unit/graphql.server.unit.ts @@ -23,7 +23,7 @@ describe('GraphQL server', () => { it('registers resolver classes', () => { server.resolver(RecipeResolver); - expect(server.getResolvers()).to.containEql(RecipeResolver); + expect(server.getResolverClasses()).to.containEql(RecipeResolver); }); it('registers resolver classes with name', () => { @@ -39,10 +39,29 @@ describe('GraphQL server', () => { return next(); }; server.middleware(middleware); - const middlewareList = await server.getMiddleware(); + const middlewareList = await server.getMiddlewareList(); expect(middlewareList).to.containEql(middleware); }); + it('fails to start without resolvers', async () => { + await expect(server.start()).to.be.rejectedWith( + /Empty `resolvers` array property found in `buildSchema` options/, + ); + }); + + it('starts and stops', async () => { + server.resolver(RecipeResolver); + await server.start(); + expect(server.listening).to.be.true(); + await server.stop(); + expect(server.listening).to.be.false(); + }); + + it('does not create http server with asMiddlewareOnly option', async () => { + server = new GraphQLServer({asMiddlewareOnly: true}); + expect(server.httpServer).to.be.undefined(); + }); + function givenServer() { server = new GraphQLServer(); } diff --git a/extensions/graphql/src/graphql.component.ts b/extensions/graphql/src/graphql.component.ts index 86d92d4f52c7..8c3b195dd8b6 100644 --- a/extensions/graphql/src/graphql.component.ts +++ b/extensions/graphql/src/graphql.component.ts @@ -4,14 +4,16 @@ // License text available at https://opensource.org/licenses/MIT import { + Application, Binding, Component, - config, + CoreBindings, createBindingFromClass, + inject, } from '@loopback/core'; import {GraphQLResolverBooter} from './booters/resolver.booter'; import {GraphQLServer} from './graphql.server'; -import {GraphQLComponentOptions} from './types'; +import {GraphQLBindings} from './keys'; /** * Component for GraphQL @@ -22,5 +24,9 @@ export class GraphQLComponent implements Component { createBindingFromClass(GraphQLResolverBooter), ]; - constructor(@config() private options: GraphQLComponentOptions = {}) {} + constructor(@inject(CoreBindings.APPLICATION_INSTANCE) app: Application) { + app + .configure(GraphQLBindings.GRAPHQL_SERVER) + .toAlias(GraphQLBindings.CONFIG); + } } diff --git a/extensions/graphql/src/graphql.server.ts b/extensions/graphql/src/graphql.server.ts index 162bf3fa2cdd..6c5512b7dadf 100644 --- a/extensions/graphql/src/graphql.server.ts +++ b/extensions/graphql/src/graphql.server.ts @@ -18,7 +18,7 @@ import { lifeCycleObserver, Server, } from '@loopback/core'; -import {HttpOptions, HttpServer} from '@loopback/http-server'; +import {HttpServer} from '@loopback/http-server'; import {ContextFunction} from 'apollo-server-core'; import { ApolloServer, @@ -37,29 +37,7 @@ import { import {Middleware} from 'type-graphql/dist/interfaces/Middleware'; import {LoopBackContainer} from './graphql.container'; import {GraphQLBindings, GraphQLTags} from './keys'; - -export {ContextFunction} from 'apollo-server-core'; -export {ApolloServerExpressConfig} from 'apollo-server-express'; -export {ExpressContext} from 'apollo-server-express/dist/ApolloServer'; -export {Middleware as GraphQLMiddleware} from 'type-graphql/dist/interfaces/Middleware'; - -/** - * Options for GraphQL server - */ -export interface GraphQLServerOptions extends HttpOptions { - /** - * GraphQL related configuration - */ - graphql?: ApolloServerExpressConfig; - /** - * Express settings - */ - express?: Record; - /** - * Use as a middleware for RestServer instead of a standalone server - */ - asMiddlewareOnly?: boolean; -} +import {GraphQLServerOptions} from './types'; /** * GraphQL Server @@ -78,13 +56,17 @@ export class GraphQLServer extends Context implements Server { parent?: Context, ) { super(parent, 'graphql-server'); + + // An internal express application for GraphQL only this.expressApp = express(); - if (options.express) { - for (const p in options.express) { - this.expressApp.set(p, options.express[p]); + if (options.expressSettings) { + for (const p in options.expressSettings) { + this.expressApp.set(p, options.expressSettings[p]); } } + // Create a standalone http server if GraphQL is mounted as an Express + // middleware to a RestServer from `@loopback/rest` if (!options.asMiddlewareOnly) { this.httpServer = new HttpServer(this.expressApp, this.options); } @@ -93,7 +75,7 @@ export class GraphQLServer extends Context implements Server { /** * Get a list of resolver classes */ - getResolvers(): Constructor>[] { + getResolverClasses(): Constructor>[] { const view = this.createView(filterByTag(GraphQLTags.RESOLVER)); return view.bindings .filter(b => b.valueConstructor != null) @@ -103,7 +85,7 @@ export class GraphQLServer extends Context implements Server { /** * Get a list of middleware */ - async getMiddleware(): Promise[]> { + async getMiddlewareList(): Promise[]> { const view = this.createView>( filterByTag(GraphQLTags.MIDDLEWARE), ); @@ -133,10 +115,11 @@ export class GraphQLServer extends Context implements Server { } async start() { - const resolverClasses = (this.getResolvers() as unknown) as NonEmptyArray< + const resolverClasses = (this.getResolverClasses() as unknown) as NonEmptyArray< Function >; + // Get the configured auth checker const authChecker: AuthChecker = (await this.get(GraphQLBindings.GRAPHQL_AUTH_CHECKER, { optional: true, @@ -157,7 +140,7 @@ export class GraphQLServer extends Context implements Server { container: new LoopBackContainer(this), authChecker, pubSub, - globalMiddlewares: await this.getMiddleware(), + globalMiddlewares: await this.getMiddlewareList(), }); // Allow a graphql context resolver to be bound to GRAPHQL_CONTEXT_RESOLVER @@ -166,26 +149,35 @@ export class GraphQLServer extends Context implements Server { optional: true, })) ?? (context => context); + // Create ApolloServerExpress GraphQL server const serverConfig: ApolloServerExpressConfig = { // enable GraphQL Playground playground: true, context: graphqlContextResolver, - subscriptions: {}, - ...this.options.graphql, + subscriptions: false, + ...this.options.apollo, schema, }; - // Create GraphQL server const graphQLServer = new ApolloServer(serverConfig); - graphQLServer.applyMiddleware({app: this.expressApp}); + // Set up subscription handlers + if (this.httpServer && serverConfig.subscriptions) { + graphQLServer.installSubscriptionHandlers(this.httpServer?.server); + } + + // Start the http server if created await this.httpServer?.start(); } async stop() { + // Stop the http server if created await this.httpServer?.stop(); } + /** + * Is the GraphQL listening + */ get listening() { return !!this.httpServer?.listening; } diff --git a/extensions/graphql/src/keys.ts b/extensions/graphql/src/keys.ts index 75f8fe8074e1..a620c85c60b6 100644 --- a/extensions/graphql/src/keys.ts +++ b/extensions/graphql/src/keys.ts @@ -3,15 +3,23 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {BindingKey, Constructor} from '@loopback/core'; +import {BindingKey, Constructor, CoreBindings} from '@loopback/core'; import {AuthChecker, PubSubEngine, ResolverData} from 'type-graphql'; import {GraphQLComponent} from './graphql.component'; -import {ContextFunction, ExpressContext, GraphQLServer} from './graphql.server'; +import {GraphQLServer} from './graphql.server'; +import {ContextFunction, ExpressContext, GraphQLServerOptions} from './types'; /** * Namespace for GraphQL related bindings */ export namespace GraphQLBindings { + /** + * Binding key for setting and injecting GraphQLServerConfig + */ + export const CONFIG: BindingKey = CoreBindings.APPLICATION_CONFIG.deepProperty( + 'graphql', + ); + /** * Binding key for the GraphQL server */ diff --git a/extensions/graphql/src/types.ts b/extensions/graphql/src/types.ts index 89d4eef7c427..41b3b2cd03ad 100644 --- a/extensions/graphql/src/types.ts +++ b/extensions/graphql/src/types.ts @@ -3,7 +3,14 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT +import {HttpOptions} from '@loopback/http-server'; +import {ApolloServerExpressConfig} from 'apollo-server-express'; + +export {ContextFunction} from 'apollo-server-core'; +export {ApolloServerExpressConfig} from 'apollo-server-express'; +export {ExpressContext} from 'apollo-server-express/dist/ApolloServer'; export {Float, ID, Int, ResolverInterface} from 'type-graphql'; +export {Middleware as GraphQLMiddleware} from 'type-graphql/dist/interfaces/Middleware'; /** * Options for GraphQL component @@ -11,3 +18,21 @@ export {Float, ID, Int, ResolverInterface} from 'type-graphql'; export interface GraphQLComponentOptions { // To be added } + +/** + * Options for GraphQL server + */ +export interface GraphQLServerOptions extends HttpOptions { + /** + * ApolloServerExpress related configuration + */ + apollo?: ApolloServerExpressConfig; + /** + * Express settings + */ + expressSettings?: Record; + /** + * Use as a middleware for RestServer instead of a standalone server + */ + asMiddlewareOnly?: boolean; +}