UNPKG

@graphql-hive/nestjs

Version:
219 lines (215 loc) • 7.5 kB
'use strict'; var gateway = require('@graphql-hive/gateway'); var logger = require('@graphql-hive/logger'); var utils = require('@graphql-tools/utils'); var common = require('@nestjs/common'); var graphql = require('@nestjs/graphql'); var promiseHelpers = require('@whatwg-node/promise-helpers'); var graphql$1 = require('graphql'); var tslib = require('tslib'); class HiveGatewayDriver extends graphql.AbstractGraphQLDriver { _gatewayRuntime; _subscriptionService; async ensureGatewayRuntime({ typeDefs, resolvers, logging, ...options }) { if (this._gatewayRuntime) { return this._gatewayRuntime; } const additionalTypeDefs = []; if (typeDefs) { additionalTypeDefs.push(typeDefs); } const additionalResolvers = []; if (resolvers) { additionalResolvers.push(...utils.asArray(resolvers)); } let log; if (logging != null) { log = gateway.createLoggerFromLogging(logging); } else { const nestLog = new common.Logger("Hive Gateway"); log = new logger.Logger({ writers: [ { write(level, attrs, msg) { switch (level) { case "trace": nestLog.verbose(msg, attrs); break; case "info": nestLog.log(msg, attrs); break; default: nestLog[level](msg, attrs); } } } ] }); } const configCtx = { log, cwd: process.cwd(), pubsub: options.pubsub || new gateway.MemPubSub() }; const cache = await gateway.getCacheInstanceFromConfig(options, configCtx); const builtinPlugins = await gateway.getBuiltinPluginsFromConfig(options, { ...configCtx, cache }); this._gatewayRuntime = gateway.createGatewayRuntime({ ...options, logging: configCtx.log, cache, graphqlEndpoint: options.path, additionalTypeDefs, additionalResolvers, disableIntrospection: options.introspection === false ? { disableIf: () => options.introspection || false } : void 0, ...options.context || options.transformSchema || options.sortSchema ? { plugins: (ctx) => { const existingPlugins = options.plugins?.(ctx) || []; if (options.context) { const contextPlugin = { onContextBuilding: ({ context, extendContext }) => promiseHelpers.handleMaybePromise( () => typeof options.context === "function" ? options.context(context) : options.context, extendContext ) }; existingPlugins.push(contextPlugin); } if (options.transformSchema) { const schemaTransformPlugin = { onSchemaChange({ schema, replaceSchema }) { return promiseHelpers.handleMaybePromise( () => options.transformSchema(schema), replaceSchema ); } }; existingPlugins.push(schemaTransformPlugin); } if (options.sortSchema) { const schemaSortPlugin = { onSchemaChange({ schema, replaceSchema }) { replaceSchema(graphql$1.lexicographicSortSchema(schema)); } }; existingPlugins.push(schemaSortPlugin); } return [...builtinPlugins, ...existingPlugins]; } } : {} }); return this._gatewayRuntime; } async start(options) { const gatewayRuntime = await this.ensureGatewayRuntime(options); const platformName = this.httpAdapterHost.httpAdapter.getType(); if (platformName === "express") { this.registerExpress(); } else if (platformName === "fastify") { this.registerFastify(); } else { throw new Error(`No support for current HttpAdapter: ${platformName}`); } if (options.installSubscriptionHandlers || options.subscriptions) { const subscriptionsOptions = options.subscriptions || { "graphql-ws": {} }; if (subscriptionsOptions["graphql-ws"]) { const gwOptions = gateway.getGraphQLWSOptions( gatewayRuntime, (ctx) => ({ req: ctx.extra?.request, socket: ctx.extra?.socket }) ); subscriptionsOptions["graphql-ws"] = { ...gwOptions, ...typeof subscriptionsOptions["graphql-ws"] === "object" ? subscriptionsOptions["graphql-ws"] : {} }; } if (subscriptionsOptions["subscriptions-transport-ws"]) { subscriptionsOptions["subscriptions-transport-ws"] = typeof subscriptionsOptions["subscriptions-transport-ws"] === "object" ? subscriptionsOptions["subscriptions-transport-ws"] : {}; subscriptionsOptions["subscriptions-transport-ws"].onOperation = async (_msg, params, ws) => { const { schema, execute, subscribe, contextFactory, parse, validate } = gatewayRuntime.getEnveloped({ ...params.context, req: ( // @ts-expect-error upgradeReq does exist but is untyped ws.upgradeReq ), socket: ws, params }); const args = { schema, operationName: params.operationName, document: typeof params.query === "string" ? parse(params.query) : params.query, variables: params.variables, context: await contextFactory(), rootValue: { execute, subscribe } }; const errors = validate(args.schema, args.document); if (errors.length) return errors; return args; }; } this._subscriptionService = new graphql.GqlSubscriptionService( { schema: await gatewayRuntime.getSchema(), path: options.path, // @ts-expect-error - We know that execute and subscribe are defined execute: (args) => args.rootValue.execute(args), // @ts-expect-error - We know that execute and subscribe are defined subscribe: (args) => args.rootValue.subscribe(args), ...subscriptionsOptions }, this.httpAdapterHost.httpAdapter?.getHttpServer() ); } } async stop() { await Promise.all([ this._subscriptionService?.stop(), this._gatewayRuntime?.dispose() ]); } async generateSchema(options) { const gatewayRuntime = await this.ensureGatewayRuntime(options); return gatewayRuntime.getSchema(); } registerExpress() { if (!this._gatewayRuntime) { throw new Error("Hive Gateway is not initialized"); } this.httpAdapterHost.httpAdapter.use(this._gatewayRuntime); } registerFastify() { this.httpAdapterHost.httpAdapter.getInstance().all("*", async (req, reply) => { if (!this._gatewayRuntime) { throw new Error("Hive Gateway is not initialized"); } const response = await this._gatewayRuntime.handleNodeRequestAndResponse(req, reply, { req, reply }); response.headers.forEach((value, key) => { reply.header(key, value); }); reply.status(response.status); reply.send(response.body); return reply; }); } } tslib.__decorate([common.Injectable()], HiveGatewayDriver); exports.HiveGatewayDriver = HiveGatewayDriver;