@graphql-mesh/http
Version:
112 lines (108 loc) • 4.25 kB
JavaScript
import { path, fs } from '@graphql-mesh/cross-helpers';
import { DefaultLogger, pathExists } from '@graphql-mesh/utils';
import { createServerAdapter } from '@whatwg-node/server';
import { Router } from 'itty-router';
import { withCookies } from 'itty-router-extras';
import { createYoga, useLogger } from 'graphql-yoga';
import { Response } from '@whatwg-node/fetch';
const graphqlHandler = (mesh$, playgroundTitle, playgroundEnabled, graphqlEndpoint, corsConfig) => {
const yoga$ = mesh$.then(mesh => createYoga({
parserCache: false,
validationCache: false,
plugins: [
...mesh.plugins,
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 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 = createServerAdapter(Router());
serverAdapter.all('/healthcheck', () => new Response(null, {
status: 200,
}));
serverAdapter.all('/readiness', () => new 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 Response(null, {
status: 204,
statusText: 'OK',
});
}
}
}
return undefined;
});
if (staticFiles) {
const indexPath = path.join(baseDir, staticFiles, 'index.html');
serverAdapter.get('*', async (request) => {
const url = new URL(request.url);
if (graphqlPath !== '/' && url.pathname === '/' && (await pathExists(indexPath))) {
const indexFile = await fs.promises.readFile(indexPath);
return new Response(indexFile, {
status: 200,
headers: {
'Content-Type': 'text/html',
},
});
}
const filePath = path.join(baseDir, staticFiles, url.pathname);
if (await pathExists(filePath)) {
const body = await fs.promises.readFile(filePath);
return new Response(body, {
status: 200,
});
}
return undefined;
});
}
else if (graphqlPath !== '/') {
serverAdapter.get('/', () => new Response(null, {
status: 302,
headers: {
Location: graphqlPath,
},
}));
}
serverAdapter.all('*', withCookies, graphqlHandler(mesh$, playgroundTitle, playgroundEnabled, graphqlPath, corsConfig));
return serverAdapter;
}
export { createMeshHTTPHandler };