2018-05-10 17:28:06 -07:00
|
|
|
import * as hapi from 'hapi';
|
|
|
|
import { createServer, Server as HttpServer } from 'http';
|
|
|
|
import { ApolloServerBase, EngineLauncherOptions } from 'apollo-server-core';
|
|
|
|
import { parseAll } from 'accept';
|
|
|
|
import { renderPlaygroundPage } from 'graphql-playground-html';
|
|
|
|
|
|
|
|
import { graphqlHapi } from './hapiApollo';
|
|
|
|
|
|
|
|
export interface ServerRegistration {
|
2018-05-22 10:08:15 -07:00
|
|
|
app?: hapi.Server;
|
|
|
|
//The options type should exclude port
|
|
|
|
options?: hapi.ServerOptions;
|
2018-05-10 17:28:06 -07:00
|
|
|
server: ApolloServerBase<hapi.Request>;
|
|
|
|
path?: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface HapiListenOptions {
|
|
|
|
port?: number | string;
|
|
|
|
host?: string; // default: ''. This is where engineproxy listens.
|
|
|
|
pipePath?: string;
|
|
|
|
graphqlPaths?: string[]; // default: ['/graphql']
|
|
|
|
innerHost?: string; // default: '127.0.0.1'. This is where Node listens.
|
|
|
|
launcherOptions?: EngineLauncherOptions;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const registerServer = async ({
|
|
|
|
app,
|
2018-05-22 10:08:15 -07:00
|
|
|
options,
|
2018-05-10 17:28:06 -07:00
|
|
|
server,
|
|
|
|
path,
|
|
|
|
}: ServerRegistration) => {
|
|
|
|
if (!path) path = '/graphql';
|
|
|
|
|
2018-05-22 10:08:15 -07:00
|
|
|
let hapiApp: hapi.Server;
|
|
|
|
if (app) {
|
|
|
|
hapiApp = app;
|
|
|
|
if (options) {
|
|
|
|
console.warn(`A Hapi Server was passed in, so the options are ignored`);
|
|
|
|
}
|
|
|
|
} else if (options) {
|
|
|
|
if ((options as any).port) {
|
|
|
|
throw new Error(`
|
|
|
|
The options for registerServer should not include a port, since autoListen is set to false. Please set the port under the http options in listen:
|
|
|
|
|
|
|
|
const server = new ApolloServer({ typeDefs, resolvers });
|
|
|
|
|
|
|
|
registerServer({
|
|
|
|
server,
|
|
|
|
options,
|
|
|
|
});
|
|
|
|
|
|
|
|
server.listen({ http: { port: YOUR_PORT_HERE } });
|
|
|
|
`);
|
|
|
|
}
|
|
|
|
hapiApp = new hapi.Server({ ...options, autoListen: false });
|
|
|
|
} else {
|
|
|
|
hapiApp = new hapi.Server({ autoListen: false });
|
|
|
|
}
|
|
|
|
|
|
|
|
await hapiApp.ext({
|
2018-05-10 17:28:06 -07:00
|
|
|
type: 'onRequest',
|
|
|
|
method: function(request, h) {
|
|
|
|
if (request.path !== path) {
|
|
|
|
return h.continue;
|
|
|
|
}
|
2018-05-10 22:12:16 -07:00
|
|
|
|
2018-05-10 17:28:06 -07:00
|
|
|
if (!server.disableTools && request.method === 'get') {
|
|
|
|
//perform more expensive content-type check only if necessary
|
2018-05-10 22:12:16 -07:00
|
|
|
const accept = parseAll(request.headers);
|
2018-05-10 17:28:06 -07:00
|
|
|
const types = accept.mediaTypes as string[];
|
|
|
|
const prefersHTML =
|
|
|
|
types.find(
|
|
|
|
(x: string) => x === 'text/html' || x === 'application/json',
|
|
|
|
) === 'text/html';
|
|
|
|
|
|
|
|
if (prefersHTML) {
|
|
|
|
return h
|
|
|
|
.response(
|
|
|
|
renderPlaygroundPage({
|
2018-05-11 17:30:52 -07:00
|
|
|
subscriptionsEndpoint: server.subscriptionsPath,
|
2018-05-10 17:28:06 -07:00
|
|
|
endpoint: path,
|
2018-05-10 22:12:16 -07:00
|
|
|
version: '1.4.0',
|
2018-05-10 17:28:06 -07:00
|
|
|
}),
|
|
|
|
)
|
2018-05-10 22:12:16 -07:00
|
|
|
.type('text/html')
|
|
|
|
.takeover();
|
2018-05-10 17:28:06 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return h.continue;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2018-05-22 10:08:15 -07:00
|
|
|
await hapiApp.register({
|
2018-05-10 17:28:06 -07:00
|
|
|
plugin: graphqlHapi,
|
|
|
|
options: {
|
|
|
|
path: path,
|
|
|
|
graphqlOptions: server.request.bind(server),
|
|
|
|
route: {
|
|
|
|
cors: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2018-05-22 10:08:15 -07:00
|
|
|
server.use({ path, getHttp: () => hapiApp.listener });
|
2018-05-10 17:28:06 -07:00
|
|
|
|
2018-05-10 22:12:16 -07:00
|
|
|
const listen = server.listen.bind(server);
|
2018-05-10 17:28:06 -07:00
|
|
|
server.listen = async options => {
|
|
|
|
//requires that autoListen is false, so that
|
|
|
|
//hapi sets up app.listener without start
|
2018-05-22 10:08:15 -07:00
|
|
|
await hapiApp.start();
|
2018-05-10 17:28:06 -07:00
|
|
|
|
2018-05-10 22:12:16 -07:00
|
|
|
//While this is not strictly necessary, it ensures that apollo server calls
|
|
|
|
//listen first, setting the port. Otherwise the hapi server constructor
|
|
|
|
//sets the port
|
2018-05-22 10:08:15 -07:00
|
|
|
if (hapiApp.listener.listening) {
|
2018-05-10 22:12:16 -07:00
|
|
|
throw Error(
|
|
|
|
`
|
|
|
|
Ensure that constructor of Hapi server sets autoListen to false, as follows:
|
|
|
|
|
|
|
|
const app = Hapi.server({
|
|
|
|
autoListen: false,
|
|
|
|
//other parameters
|
|
|
|
});
|
|
|
|
`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2018-05-10 17:28:06 -07:00
|
|
|
//starts the hapi listener at a random port when engine proxy used,
|
|
|
|
//otherwise will start the server at the provided port
|
|
|
|
return listen({ ...options });
|
|
|
|
};
|
|
|
|
};
|