diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index fb9a31e1146..f3c9e1ca63a 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -101,15 +101,17 @@ export class ApolloServerBase { const env = process.env.NODE_ENV; const isDev = env !== 'production' && env !== 'test'; - // if this is local dev, we want graphql gui and introspection to be turned on - // in production, you can manually turn these on by passing { introspection: true } - // to the constructor of ApolloServer // we use this.disableTools to track this internally for later use when - // constructing middleware by frameworks - if (typeof introspection === 'boolean') this.disableTools = !introspection; - else this.disableTools = !isDev; + // constructing middleware by frameworks to disable graphql playground + this.disableTools = !isDev; - if (this.disableTools) { + // if this is local dev, introspection should turned on + // in production, we can manually turn introspection on by passing { + // introspection: true } to the constructor of ApolloServer + if ( + (typeof introspection === 'boolean' && !introspection) || + (introspection === undefined && !isDev) + ) { const noIntro = [NoIntrospection]; requestOptions.validationRules = requestOptions.validationRules ? requestOptions.validationRules.concat(noIntro) diff --git a/packages/apollo-server-express/src/ApolloServer.test.ts b/packages/apollo-server-express/src/ApolloServer.test.ts index e9a40d8a9f3..a890cdfeef4 100644 --- a/packages/apollo-server-express/src/ApolloServer.test.ts +++ b/packages/apollo-server-express/src/ApolloServer.test.ts @@ -73,6 +73,60 @@ describe('apollo-server-express', () => { expect(result.errors, 'errors should exist').not.to.exist; }); + it('can enable gui separately from instrospection during production', async () => { + const INTROSPECTION_QUERY = ` + { + __schema { + directives { + name + } + } + } +`; + const nodeEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'production'; + + server = new ApolloServer({ + typeDefs, + resolvers, + }); + app = express(); + + registerServer({ app, server, enableGUI: true }); + + const { url } = await server.listen(); + const apolloFetch = createApolloFetch({ uri: url }); + const result = await apolloFetch({ query: INTROSPECTION_QUERY }); + + expect(result.errors.length).to.equal(1); + expect(result.errors[0].extensions.code).to.equal( + 'GRAPHQL_VALIDATION_FAILED', + ); + + return new Promise((resolve, reject) => { + request( + { + url, + method: 'GET', + headers: { + accept: + 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', + }, + }, + (error, response, body) => { + process.env.NODE_ENV = nodeEnv; + if (error) { + reject(error); + } else { + expect(body).to.contain('GraphQLPlayground'); + expect(response.statusCode).to.equal(200); + resolve(); + } + }, + ); + }); + }); + it('renders GraphQL playground when browser requests', async () => { const nodeEnv = process.env.NODE_ENV; delete process.env.NODE_ENV; @@ -172,6 +226,7 @@ describe('apollo-server-express', () => { registerServer({ app, server, bodyParserConfig: { limit: 0 } }); const { port } = await server.listen(); + return new Promise((resolve, reject) => { request( { @@ -260,7 +315,7 @@ describe('apollo-server-express', () => { }); }); describe('file uploads', () => { - xit('enabled uploads', async () => { + it('enabled uploads', async () => { server = new ApolloServer({ typeDefs: gql` type File { diff --git a/packages/apollo-server-express/src/ApolloServer.ts b/packages/apollo-server-express/src/ApolloServer.ts index 55c730dbfac..9c6b65086d6 100644 --- a/packages/apollo-server-express/src/ApolloServer.ts +++ b/packages/apollo-server-express/src/ApolloServer.ts @@ -38,6 +38,7 @@ export interface ServerRegistration { bodyParserConfig?: OptionsJson; onHealthCheck?: (req: express.Request) => Promise; disableHealthCheck?: boolean; + enableGUI?: boolean; //https://github.com/jaydenseric/apollo-upload-server#options uploads?: boolean | Record; } @@ -79,6 +80,7 @@ export const registerServer = async ({ cors, bodyParserConfig, disableHealthCheck, + enableGUI, onHealthCheck, uploads, }: ServerRegistration) => { @@ -132,7 +134,11 @@ export const registerServer = async ({ uploadsMiddleware ? uploadsMiddleware : (_req, _res, next) => next(), (req, res, next) => { // make sure we check to see if graphql gui should be on - if (!server.disableTools && req.method === 'GET') { + // enableGUI takes precedence over the server tools setting + if ( + (enableGUI || (enableGUI === undefined && !server.disableTools)) && + req.method === 'GET' + ) { //perform more expensive content-type check only if necessary const accept = accepts(req); const types = accept.types() as string[]; diff --git a/packages/apollo-server-hapi/src/ApolloServer.ts b/packages/apollo-server-hapi/src/ApolloServer.ts index 6b89c5b1d66..00a4b85b076 100644 --- a/packages/apollo-server-hapi/src/ApolloServer.ts +++ b/packages/apollo-server-hapi/src/ApolloServer.ts @@ -35,6 +35,7 @@ export interface ServerRegistration { cors?: boolean; onHealthCheck?: (request: hapi.Request) => Promise; disableHealthCheck?: boolean; + enableGUI?: boolean; uploads?: boolean | Record; } @@ -65,6 +66,7 @@ export const registerServer = async ({ cors, path, disableHealthCheck, + enableGUI, onHealthCheck, uploads, }: ServerRegistration) => { @@ -118,7 +120,11 @@ server.listen({ http: { port: YOUR_PORT_HERE } }); ); } - if (!server.disableTools && request.method === 'get') { + // enableGUI takes precedence over the server tools setting + if ( + (enableGUI || (enableGUI === undefined && !server.disableTools)) && + request.method === 'get' + ) { //perform more expensive content-type check only if necessary const accept = parseAll(request.headers); const types = accept.mediaTypes as string[];