@graphql-mesh/serve-cli
Version:
124 lines (123 loc) • 4.9 kB
JavaScript
import { promises as fsPromises } from 'node:fs';
import { createServer as createHTTPServer } from 'node:http';
import { createServer as createHTTPSServer } from 'node:https';
import { createAsyncDisposable, getTerminateStack } from '@graphql-mesh/utils';
import { defaultOptions } from './cli.js';
export async function startServerForRuntime(runtime, { log, host = defaultOptions.host, port = defaultOptions.port, sslCredentials, maxHeaderSize = 16_384, disableWebsockets = false, }) {
const terminateStack = getTerminateStack();
terminateStack.use(runtime);
process.on('message', message => {
if (message === 'invalidateUnifiedGraph') {
log.info(`Invalidating Supergraph`);
runtime.invalidateUnifiedGraph();
}
});
const serverOpts = {
log,
host,
port,
sslCredentials,
maxHeaderSize,
disableWebsockets,
};
const server = await startNodeHttpServer(runtime, serverOpts);
terminateStack.use(server);
return server;
}
async function startNodeHttpServer(gwRuntime, opts) {
const { log, host = defaultOptions.host, port = defaultOptions.port, sslCredentials, maxHeaderSize, disableWebsockets, } = opts;
let server;
let protocol;
if (sslCredentials) {
protocol = 'https';
const sslOptionsForNodeHttp = {};
if (sslCredentials.ca_file_name) {
sslOptionsForNodeHttp.ca = await fsPromises.readFile(sslCredentials.ca_file_name);
}
if (sslCredentials.cert_file_name) {
sslOptionsForNodeHttp.cert = await fsPromises.readFile(sslCredentials.cert_file_name);
}
if (sslCredentials.dh_params_file_name) {
sslOptionsForNodeHttp.dhparam = await fsPromises.readFile(sslCredentials.dh_params_file_name);
}
if (sslCredentials.key_file_name) {
sslOptionsForNodeHttp.key = await fsPromises.readFile(sslCredentials.key_file_name);
}
if (sslCredentials.passphrase) {
sslOptionsForNodeHttp.passphrase = sslCredentials.passphrase;
}
if (sslCredentials.ssl_ciphers) {
sslOptionsForNodeHttp.ciphers = sslCredentials.ssl_ciphers;
}
if (sslCredentials.ssl_prefer_low_memory_usage) {
sslOptionsForNodeHttp.honorCipherOrder = true;
}
server = createHTTPSServer({
...sslOptionsForNodeHttp,
maxHeaderSize,
}, gwRuntime);
}
else {
protocol = 'http';
server = createHTTPServer({
maxHeaderSize,
}, gwRuntime);
}
const url = `${protocol}://${host}:${port}`.replace('0.0.0.0', 'localhost');
log.debug(`Starting server on ${url}`);
if (!disableWebsockets) {
const { WebSocketServer } = await import('ws');
const wsServer = new WebSocketServer({
path: gwRuntime.graphqlEndpoint,
server,
});
const graphqlWSOptions = getGraphQLWSOptions(gwRuntime);
const { useServer } = await import('graphql-ws/lib/use/ws');
useServer(graphqlWSOptions, wsServer);
}
return new Promise((resolve, reject) => {
server.once('error', reject);
server.listen(port, host, () => {
log.info(`Listening on ${url}`);
resolve(createAsyncDisposable(() => new Promise(resolve => {
process.stderr.write('\n');
log.info(`Stopping the server`);
server.closeAllConnections();
server.close(() => {
log.info(`Stopped the server successfully`);
resolve();
});
})));
});
});
}
export function getGraphQLWSOptions(gwRuntime) {
return {
execute: (args) => args.rootValue.execute(args),
subscribe: (args) => args.rootValue.subscribe(args),
onSubscribe: async (ctx, msg) => {
const { schema, execute, subscribe, contextFactory, parse, validate } = gwRuntime.getEnveloped({
connectionParams: ctx.connectionParams,
req: ctx.extra.request,
});
const args = {
schema: schema || (await gwRuntime.getSchema()),
operationName: msg.payload.operationName,
document: parse(msg.payload.query),
variableValues: msg.payload.variables,
contextValue: await contextFactory(),
rootValue: {
execute,
subscribe,
},
};
if (args.schema) {
const errors = validate(args.schema, args.document);
if (errors.length)
return errors;
}
return args;
},
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
};
}