@graphql-mesh/serve-runtime
Version:
110 lines (109 loc) • 4.25 kB
JavaScript
import { createYoga } from 'graphql-yoga';
import { convertSupergraphToFusiongraph } from '@graphql-mesh/fusion-federation';
import { useFusiongraph } from '@graphql-mesh/fusion-runtime';
// eslint-disable-next-line import/no-extraneous-dependencies
import { DefaultLogger, getHeadersObj, wrapFetchWithHooks } from '@graphql-mesh/utils';
import { buildHTTPExecutor } from '@graphql-tools/executor-http';
import { useExecutor } from '@graphql-tools/executor-yoga';
import { isPromise } from '@graphql-tools/utils';
import { handleUnifiedGraphConfig } from './handleUnifiedGraphConfig.js';
export function createServeRuntime(config) {
let fetchAPI = config.fetchAPI;
// eslint-disable-next-line prefer-const
let logger;
let wrappedFetchFn;
const serveContext = {
get fetch() {
return wrappedFetchFn;
},
get logger() {
return logger;
},
cwd: globalThis.process?.cwd(),
cache: 'cache' in config ? config.cache : undefined,
pubsub: 'pubsub' in config ? config.pubsub : undefined,
};
let supergraphYogaPlugin;
if ('http' in config) {
const executor = buildHTTPExecutor({
fetch: fetchAPI?.fetch,
...config.http,
});
supergraphYogaPlugin = useExecutor(executor);
}
else if ('fusiongraph' in config) {
supergraphYogaPlugin = useFusiongraph({
getFusiongraph() {
return handleUnifiedGraphConfig(config.fusiongraph || './fusiongraph.graphql', serveContext);
},
transports: config.transports,
polling: config.polling,
additionalResolvers: config.additionalResolvers,
transportBaseContext: serveContext,
});
}
else if ('supergraph' in config) {
supergraphYogaPlugin = useFusiongraph({
getFusiongraph() {
const supergraph$ = handleUnifiedGraphConfig(config.supergraph || './supergraph.graphql', serveContext);
serveContext.logger?.info?.(`Converting Federation Supergraph to Fusiongraph`);
if (isPromise(supergraph$)) {
return supergraph$.then(supergraph => convertSupergraphToFusiongraph(supergraph));
}
return convertSupergraphToFusiongraph(supergraph$);
},
transports: config.transports,
polling: config.polling,
additionalResolvers: config.additionalResolvers,
transportBaseContext: serveContext,
});
}
const defaultFetchPlugin = {
onFetch({ setFetchFn }) {
setFetchFn(fetchAPI.fetch);
},
onYogaInit({ yoga }) {
const onFetchHooks = [];
for (const plugin of yoga.getEnveloped._plugins) {
if (plugin.onFetch) {
onFetchHooks.push(plugin.onFetch);
}
}
wrappedFetchFn = wrapFetchWithHooks(onFetchHooks);
},
};
const yoga = createYoga({
fetchAPI: config.fetchAPI,
logging: config.logging == null ? new DefaultLogger() : config.logging,
plugins: [defaultFetchPlugin, supergraphYogaPlugin, ...(config.plugins?.(serveContext) || [])],
context({ request, req, connectionParams }) {
// Maybe Node-like environment
if (req?.headers) {
return {
headers: getHeadersObj(req.headers),
connectionParams,
};
}
// Fetch environment
if (request?.headers) {
return {
headers: getHeadersObj(request.headers),
connectionParams,
};
}
return undefined;
},
cors: config.cors,
graphiql: config.graphiql,
batching: config.batching,
graphqlEndpoint: config.graphqlEndpoint,
maskedErrors: config.maskedErrors,
});
fetchAPI ||= yoga.fetchAPI;
logger = yoga.logger;
Object.defineProperty(yoga, 'invalidateUnifiedGraph', {
value: supergraphYogaPlugin.invalidateUnifiedGraph,
configurable: true,
});
return yoga;
}