NestJS GraphQL订阅连接失败:无法连接WebSocket端点
1. 自定义WebSocket适配器与graphql-ws是否存在冲突?
是的,存在冲突。当你通过app.useWebSocketAdapter(new WsAdapter(app))设置全局WebSocket适配器时,它会接管服务器的所有WebSocket连接处理逻辑,覆盖Apollo Server为GraphQL订阅(/graphql端点)预设的graphql-ws处理机制,导致订阅无法建立连接。
2. 如何让GraphQL订阅与自定义WebSocket网关共存?
核心思路是避免全局覆盖WebSocket处理逻辑,仅将自定义适配器应用到需要它的WebSocket网关,而非全局设置。具体步骤:
步骤1:移除全局WebSocket适配器配置
在main.ts中删除以下代码行:
app.useWebSocketAdapter(new WsAdapter(app));
步骤2:为单个WebSocket网关指定适配器
在你的自定义WebSocket网关类上,通过@WebSocketGateway装饰器的adapter选项指定WsAdapter:
import { WebSocketGateway } from '@nestjs/websockets'; import { WsAdapter } from '@nestjs/platform-ws'; @WebSocketGateway({ adapter: WsAdapter, // 可添加其他网关配置,如path、port等 }) export class YourCustomGateway { // 网关逻辑... }
这样,只有该自定义网关会使用WsAdapter,而Apollo Server的GraphQL订阅仍能正常处理/graphql端点的WebSocket连接。
3. 集成订阅是否需要额外配置?
除了上述调整,还需确认以下配置是否正确:
确认Apollo订阅配置
确保app.module.ts中的GraphQL订阅配置正确启用graphql-ws:
GraphQLModule.forRoot<ApolloDriverConfig>({ driver: ApolloDriver, autoSchemaFile: join(process.cwd(), 'src/schema.gql'), sortSchema: true, subscriptions: { 'graphql-ws': { // 可添加graphql-ws的额外配置,如onConnect、onDisconnect等 }, }, });
将true替换为配置对象可按需添加自定义逻辑,但基础启用只需设置为true即可。
验证依赖包完整性
确保已安装所有必需的依赖:
npm install graphql-ws @nestjs/graphql @nestjs/apollo apollo-server-express
测试订阅连接
调整配置后重启服务,使用GraphQL Playground或客户端工具测试订阅连接,端点应为ws://localhost:3000/graphql。
替代方案:使用混合适配器(高级场景)
如果必须全局设置适配器(例如多个网关共用同一适配器),可创建一个混合适配器,同时处理Apollo的GraphQL订阅和自定义WebSocket网关:
import { WsAdapter } from '@nestjs/platform-ws'; import { Server } from 'ws'; import { useServer } from 'graphql-ws/lib/use/ws'; import { GraphQLSchema } from 'graphql'; export class HybridWsAdapter extends WsAdapter { constructor( app: any, private readonly schema: GraphQLSchema, ) { super(app); } create(server: any): Server { const wsServer = super.create(server); // 为/graphql端点设置graphql-ws处理 useServer( { schema: this.schema, }, { server: wsServer, path: '/graphql', }, ); return wsServer; } }
然后在main.ts中使用该混合适配器:
import { getSchema } from '@nestjs/graphql'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.setGlobalPrefix('api'); app.useGlobalPipes(new ValidationPipe()); // 获取GraphQL schema并初始化混合适配器 const schema = await app.get(getSchema()); app.useWebSocketAdapter(new HybridWsAdapter(app, schema)); await app.listen(3000); } bootstrap();
此方案适合需要全局适配器的复杂场景,但推荐优先使用单网关指定适配器的简单方案。
内容的提问来源于stack exchange,提问作者e-lab innovations

