diff --git a/packages/apollo-server-hapi/src/ApolloServer.ts b/packages/apollo-server-hapi/src/ApolloServer.ts index bf5351c6f14..f14328eb081 100644 --- a/packages/apollo-server-hapi/src/ApolloServer.ts +++ b/packages/apollo-server-hapi/src/ApolloServer.ts @@ -5,13 +5,15 @@ import { RenderPageOptions as PlaygroundRenderPageOptions, } from '@apollographql/graphql-playground-html'; -import { plugin as graphqlHapi, HapiPluginOptions } from './hapiApollo'; - export { GraphQLOptions } from 'apollo-server-core'; import { ApolloServerBase, + convertNodeHttpToRequest, GraphQLOptions, + HttpQueryError, + runHttpQuery, } from 'apollo-server-core'; +import Boom from '@hapi/boom'; export class ApolloServer extends ApolloServerBase { // This translates the arguments from the middleware into graphQL options It @@ -39,7 +41,7 @@ export class ApolloServer extends ApolloServerBase { if (!path) path = '/graphql'; - await app.ext({ + app.ext({ type: 'onRequest', method: async (request, h) => { if (request.path !== path) { @@ -72,13 +74,13 @@ export class ApolloServer extends ApolloServerBase { }); if (!disableHealthCheck) { - await app.route({ + app.route({ method: '*', path: '/.well-known/apollo/server-health', options: { cors: cors !== undefined ? cors : true, }, - handler: async function(request, h) { + handler: async function (request, h) { if (onHealthCheck) { try { await onHealthCheck(request); @@ -96,17 +98,62 @@ export class ApolloServer extends ApolloServerBase { }); } - await app.register({ - plugin: graphqlHapi, - options: { - path, - graphqlOptions: this.createGraphQLServerOptions.bind(this), - route: - route !== undefined - ? route - : { - cors: cors !== undefined ? cors : true, - }, + app.route({ + method: ['GET', 'POST'], + path, + options: route ?? { + cors: cors ?? true, + }, + handler: async (request, h) => { + try { + const { graphqlResponse, responseInit } = await runHttpQuery( + [request, h], + { + method: request.method.toUpperCase(), + options: () => this.createGraphQLServerOptions(request, h), + query: + request.method === 'post' + ? // TODO type payload as string or Record + (request.payload as any) + : request.query, + request: convertNodeHttpToRequest(request.raw.req), + }, + ); + + const response = h.response(graphqlResponse); + if (responseInit.headers) { + Object.entries( + responseInit.headers, + ).forEach(([headerName, value]) => + response.header(headerName, value), + ); + } + return response; + } catch (e: unknown) { + const error = e as HttpQueryError; + if ('HttpQueryError' !== error.name) { + throw Boom.boomify(error); + } + + if (true === error.isGraphQLError) { + const response = h.response(error.message); + response.code(error.statusCode); + response.type('application/json'); + return response; + } + + const err = new Boom.Boom(error.message, { + statusCode: error.statusCode, + }); + if (error.headers) { + Object.entries(error.headers).forEach(([headerName, value]) => { + err.output.headers[headerName] = value; + }); + } + // Boom hides the error when status code is 500 + err.output.payload.message = error.message; + throw err; + } }, }); @@ -115,7 +162,7 @@ export class ApolloServer extends ApolloServerBase { } export interface ServerRegistration { - app?: hapi.Server; + app: hapi.Server; path?: string; cors?: boolean | hapi.RouteOptionsCors; route?: hapi.RouteOptions; diff --git a/packages/apollo-server-hapi/src/hapiApollo.ts b/packages/apollo-server-hapi/src/hapiApollo.ts deleted file mode 100644 index f7e13112d64..00000000000 --- a/packages/apollo-server-hapi/src/hapiApollo.ts +++ /dev/null @@ -1,96 +0,0 @@ -import Boom from '@hapi/boom'; -import type { - Server, - Request, - RouteOptions, - ResponseToolkit, - Plugin as IPlugin -} from '@hapi/hapi'; -import { - GraphQLOptions, - runHttpQuery, - convertNodeHttpToRequest, -} from 'apollo-server-core'; -import { ValueOrPromise } from 'apollo-server-types'; - -export interface IRegister { - (server: Server, options: any, next?: Function): void; -} - -export interface HapiOptionsFunction { - (request: Request, h: ResponseToolkit): ValueOrPromise; -} - -export interface HapiPluginOptions { - path: string; - vhost?: string; - route?: RouteOptions; - graphqlOptions: GraphQLOptions | HapiOptionsFunction; -} - -const plugin: IPlugin = { - name: 'graphql', - register: (server, options, next?) => { - if (!options || !options.graphqlOptions) { - throw new Error('Apollo Server requires options.'); - } - server.route({ - method: ['GET', 'POST'], - path: options.path || '/graphql', - vhost: options.vhost || undefined, - options: options.route || {}, - handler: async (request, h) => { - try { - const { graphqlResponse, responseInit } = await runHttpQuery( - [request, h], - { - method: request.method.toUpperCase(), - options: options.graphqlOptions, - query: - request.method === 'post' - ? // TODO type payload as string or Record - (request.payload as any) - : request.query, - request: convertNodeHttpToRequest(request.raw.req), - }, - ); - - const response = h.response(graphqlResponse); - Object.keys(responseInit.headers).forEach(key => - response.header(key, responseInit.headers[key]), - ); - return response; - } catch (error) { - if ('HttpQueryError' !== error.name) { - throw Boom.boomify(error); - } - - if (true === error.isGraphQLError) { - const response = h.response(error.message); - response.code(error.statusCode); - response.type('application/json'); - return response; - } - - const err = new Boom.Boom(error.message, { statusCode: error.statusCode }); - if (error.headers) { - Object.keys(error.headers).forEach(header => { - err.output.headers[header] = error.headers[header]; - }); - } - // Boom hides the error when status code is 500 - err.output.payload.message = error.message; - throw err; - } - }, - }); - - if (next) { - next(); - } - }, -}; - -export { - plugin -}; diff --git a/packages/apollo-server-hapi/tsconfig.json b/packages/apollo-server-hapi/tsconfig.json index 40916b34767..e34feba8eb6 100644 --- a/packages/apollo-server-hapi/tsconfig.json +++ b/packages/apollo-server-hapi/tsconfig.json @@ -2,9 +2,7 @@ "extends": "../../tsconfig.base", "compilerOptions": { "rootDir": "./src", - "outDir": "./dist", - "noImplicitAny": false, - "strictNullChecks": false + "outDir": "./dist" }, "include": ["src/**/*"], "exclude": ["**/__tests__"],