@graphql-mesh/http
Version:
116 lines (110 loc) • 4.49 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
const crossHelpers = require('@graphql-mesh/cross-helpers');
const utils = require('@graphql-mesh/utils');
const server = require('@whatwg-node/server');
const ittyRouter = require('itty-router');
const ittyRouterExtras = require('itty-router-extras');
const graphqlYoga = require('graphql-yoga');
const fetch = require('@whatwg-node/fetch');
const graphqlHandler = (mesh$, playgroundTitle, playgroundEnabled, graphqlEndpoint, corsConfig) => {
const yoga$ = mesh$.then(mesh => graphqlYoga.createYoga({
parserCache: false,
validationCache: false,
plugins: [
...mesh.plugins,
graphqlYoga.useLogger({
skipIntrospection: true,
logFn: (eventName, { args }) => {
if (eventName.endsWith('-start')) {
mesh.logger.debug(`\t headers: `, args.contextValue.headers);
}
},
}),
],
logging: mesh.logger,
maskedErrors: false,
graphiql: playgroundEnabled && {
title: playgroundTitle,
},
cors: corsConfig,
graphqlEndpoint,
landingPage: false,
}));
return async (request, ...args) => {
const yoga = await yoga$;
return yoga.handle(request, ...args);
};
};
function createMeshHTTPHandler({ baseDir, getBuiltMesh, rawServeConfig = {}, playgroundTitle, }) {
let readyFlag = false;
let logger = new utils.DefaultLogger('Mesh HTTP');
const mesh$ = getBuiltMesh().then(mesh => {
readyFlag = true;
logger = mesh.logger.child('HTTP');
return mesh;
});
const { cors: corsConfig, staticFiles, playground: playgroundEnabled, endpoint: graphqlPath = '/graphql',
// TODO
// trustProxy = 'loopback',
} = rawServeConfig;
const serverAdapter = server.createServerAdapter(ittyRouter.Router());
serverAdapter.all('/healthcheck', () => new fetch.Response(null, {
status: 200,
}));
serverAdapter.all('/readiness', () => new fetch.Response(null, {
status: readyFlag ? 204 : 503,
}));
serverAdapter.post('*', async (request) => {
if (readyFlag) {
const { pubsub } = await mesh$;
for (const eventName of pubsub.getEventNames()) {
const { pathname } = new URL(request.url);
if (eventName === `webhook:${request.method.toLowerCase()}:${pathname}`) {
const body = await request.text();
logger.debug(`Received webhook request for ${pathname}`, body);
pubsub.publish(eventName, request.headers.get('content-type') === 'application/json' ? JSON.parse(body) : body);
return new fetch.Response(null, {
status: 204,
statusText: 'OK',
});
}
}
}
return undefined;
});
if (staticFiles) {
const indexPath = crossHelpers.path.join(baseDir, staticFiles, 'index.html');
serverAdapter.get('*', async (request) => {
const url = new URL(request.url);
if (graphqlPath !== '/' && url.pathname === '/' && (await utils.pathExists(indexPath))) {
const indexFile = await crossHelpers.fs.promises.readFile(indexPath);
return new fetch.Response(indexFile, {
status: 200,
headers: {
'Content-Type': 'text/html',
},
});
}
const filePath = crossHelpers.path.join(baseDir, staticFiles, url.pathname);
if (await utils.pathExists(filePath)) {
const body = await crossHelpers.fs.promises.readFile(filePath);
return new fetch.Response(body, {
status: 200,
});
}
return undefined;
});
}
else if (graphqlPath !== '/') {
serverAdapter.get('/', () => new fetch.Response(null, {
status: 302,
headers: {
Location: graphqlPath,
},
}));
}
serverAdapter.all('*', ittyRouterExtras.withCookies, graphqlHandler(mesh$, playgroundTitle, playgroundEnabled, graphqlPath, corsConfig));
return serverAdapter;
}
exports.createMeshHTTPHandler = createMeshHTTPHandler;