UNPKG

@graphql-mesh/http

Version:
112 lines (108 loc) 4.25 kB
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 };