diff --git a/package.json b/package.json index 6424fa0be7..5ed55752c7 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,6 @@ "@golevelup/nestjs-discovery": "^4.0.0", "@graphql-hive/yoga": "^0.38.2", "@graphql-tools/utils": "^10.5.4", - "@graphql-yoga/nestjs": "^3.7.0", "@graphql-yoga/plugin-apq": "^3.7.0", "@leeoniya/ufuzzy": "^1.0.11", "@nestjs/common": "^10.2.7", diff --git a/src/core/graphql/driver.ts b/src/core/graphql/driver.ts index ef273f0af8..4ed47138dd 100644 --- a/src/core/graphql/driver.ts +++ b/src/core/graphql/driver.ts @@ -1,11 +1,33 @@ import { DiscoveryService } from '@golevelup/nestjs-discovery'; -import { YogaDriver, YogaDriverConfig } from '@graphql-yoga/nestjs'; import { Injectable } from '@nestjs/common'; -import { HttpAdapter } from '../http'; +import { + AbstractGraphQLDriver as AbstractDriver, + GqlModuleOptions, +} from '@nestjs/graphql'; +import type { RouteOptions as FastifyRoute } from 'fastify'; +import { + createYoga, + YogaServerInstance, + YogaServerOptions, +} from 'graphql-yoga'; +import { GqlContextType } from '~/common'; +import { HttpAdapter, IRequest } from '../http'; +import { IResponse } from '../http/types'; import { Plugin } from './plugin.decorator'; +export interface ServerContext { + /** Cannot be `request` as {@link import('graphql-yoga').YogaInitialContext.request} overrides it */ + req: IRequest; + response: IResponse; +} + +export type DriverConfig = GqlModuleOptions & + Omit, 'context' | 'schema'>; + @Injectable() -export class Driver extends YogaDriver<'fastify'> { +export class Driver extends AbstractDriver { + private yoga: YogaServerInstance; + constructor( private readonly discovery: DiscoveryService, private readonly http: HttpAdapter, @@ -13,7 +35,9 @@ export class Driver extends YogaDriver<'fastify'> { super(); } - async start(options: YogaDriverConfig<'fastify'>) { + async start(options: DriverConfig) { + const fastify = this.http.getInstance(); + // Do our plugin discovery / registration const discoveredPlugins = await this.discovery.providersWithMetaAtKey( Plugin.KEY, @@ -23,12 +47,36 @@ export class Driver extends YogaDriver<'fastify'> { ...new Set(discoveredPlugins.map((cls) => cls.discoveredClass.instance)), ]; - await super.start(options); + this.yoga = createYoga({ + ...options, + graphqlEndpoint: options.path, + logging: false, + }); + + fastify.route({ + method: ['GET', 'POST', 'OPTIONS'], + url: this.yoga.graphqlEndpoint, + handler: this.httpHandler, + }); // Setup file upload handling - const fastify = this.http.getInstance(); fastify.addContentTypeParser('multipart/form-data', (req, payload, done) => done(null), ); } + + httpHandler: FastifyRoute['handler'] = async (req, reply) => { + const res = await this.yoga.handleNodeRequestAndResponse(req, reply, { + req, + response: reply, + }); + return await reply + .headers(Object.fromEntries(res.headers)) + .status(res.status) + .send(res.body); + }; + + async stop() { + // noop + } } diff --git a/src/core/graphql/graphql.options.ts b/src/core/graphql/graphql.options.ts index a8c21f3c83..6659f27041 100644 --- a/src/core/graphql/graphql.options.ts +++ b/src/core/graphql/graphql.options.ts @@ -1,8 +1,4 @@ import { useHive } from '@graphql-hive/yoga'; -import { - YogaDriverConfig as DriverConfig, - YogaDriverServerContext, -} from '@graphql-yoga/nestjs'; import { useAPQ } from '@graphql-yoga/plugin-apq'; import { Injectable } from '@nestjs/common'; import { GqlOptionsFactory } from '@nestjs/graphql'; @@ -20,11 +16,11 @@ import { getRegisteredScalars } from '~/common/scalars'; import { ConfigService } from '../config/config.service'; import { VersionService } from '../config/version.service'; import { apolloExplorer } from './apollo-explorer'; +import { DriverConfig, ServerContext } from './driver'; import { isGqlContext } from './gql-context.host'; import { GraphqlTracingPlugin } from './graphql-tracing.plugin'; type Plugin = PluginNoContext; -type ServerContext = YogaDriverServerContext<'fastify'>; @Injectable() export class GraphqlOptions implements GqlOptionsFactory { @@ -96,14 +92,10 @@ export class GraphqlOptions implements GqlOptionsFactory { }; } - context = ({ - req: request, - reply: response, - }: ServerContext): Partial => { + context = ({ req: request }: ServerContext): Partial => { return { [isGqlContext.KEY]: true, request, - response, session$: new BehaviorSubject(undefined), }; }; diff --git a/src/core/graphql/plugin.decorator.ts b/src/core/graphql/plugin.decorator.ts index 890fe02db0..479cfb9c3b 100644 --- a/src/core/graphql/plugin.decorator.ts +++ b/src/core/graphql/plugin.decorator.ts @@ -1,13 +1,10 @@ -import { YogaDriverServerContext } from '@graphql-yoga/nestjs'; import { createMetadataDecorator } from '@seedcompany/nest'; import { Plugin as PluginNoContext } from 'graphql-yoga'; import { GqlContextType } from '~/common'; +import { ServerContext } from './driver'; export const Plugin = createMetadataDecorator({ types: ['class'], }); -export type Plugin = PluginNoContext< - GqlContextType, - YogaDriverServerContext<'fastify'> ->; +export type Plugin = PluginNoContext; diff --git a/yarn.lock b/yarn.lock index 4a512632b0..00842b168f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1958,19 +1958,6 @@ __metadata: languageName: node linkType: hard -"@graphql-yoga/nestjs@npm:^3.7.0": - version: 3.7.0 - resolution: "@graphql-yoga/nestjs@npm:3.7.0" - peerDependencies: - "@nestjs/common": ^10.0.0 - "@nestjs/core": ^10.0.0 - "@nestjs/graphql": ^12.0.0 - graphql: ^15.0.0 || ^16.0.0 - graphql-yoga: ^5.7.0 - checksum: 10c0/459f524504c276b2ddab6759d3d10ef1762c44f7907ff71c0f930d26fb9076441267ac4f764e4131ed6d08068d02a372990339259c0af49a8400e3ede29318f6 - languageName: node - linkType: hard - "@graphql-yoga/plugin-apq@npm:^3.7.0": version: 3.7.0 resolution: "@graphql-yoga/plugin-apq@npm:3.7.0" @@ -5663,7 +5650,6 @@ __metadata: "@graphql-hive/cli": "npm:^0.44.2" "@graphql-hive/yoga": "npm:^0.38.2" "@graphql-tools/utils": "npm:^10.5.4" - "@graphql-yoga/nestjs": "npm:^3.7.0" "@graphql-yoga/plugin-apq": "npm:^3.7.0" "@leeoniya/ufuzzy": "npm:^1.0.11" "@nestjs/cli": "npm:^10.2.1"