我正在开发一个 keystone 应用程序,我们想在其中使用 graphql 订阅。
遵循文档: https://keystonejs.com/docs/config/config#extend-http-server
以及 websocket 文件的 keytone github 示例: https://github.com/keystonejs/keystone/blob/main/examples/extend-graphql-subscriptions/websocket.ts
创建的订阅出现在 Apollo Playground 架构中。
只要我从代码中注释掉 graphql-ws,websocket 连接就可以工作。当我添加来自 graphql-ws 的 wsUseServer 时,它在这两次尝试中给出以下错误:
我的 websocket.ts 代码与示例相同,除了用于调试的日志和注释测试行:
import type http from 'http'
import { useServer as wsUseServer } from 'graphql-ws/lib/use/ws'
import { WebSocketServer } from 'ws'
import { PubSub } from 'graphql-subscriptions'
import { parse } from 'graphql'
import { type Context } from '.keystone/types'
// Setup pubsub as a Global variable in dev so it survives Hot Reloads.
declare global {
let graphqlSubscriptionPubSub: PubSub
}
// The 'graphql-subscriptions' pubsub library is not recommended for production use, but can be useful as an example
// for details see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#the-pubsub-class
export const pubSub = global.graphqlSubscriptionPubSub || new PubSub()
globalThis.graphqlSubscriptionPubSub = pubSub
export function extendHttpServer (
httpServer: http.Server,
commonContext: Context,
) {
console.log('HTTP Server:', httpServer);
console.log(commonContext.graphql.schema)
// Setup WebSocket server using 'ws'
const wss = new WebSocketServer({
server: httpServer,
path: '/api/graphql',
/*handleProtocols: (protocols) => {
return Array.from(protocols).includes('graphql-ws') ? 'graphql-ws' : null;
}*/
})
// Setup the WebSocket to handle GraphQL subscriptions using 'graphql-ws'
wsUseServer(
{
schema: commonContext.graphql.schema,
// run these onSubscribe functions as needed or remove them if you don't need them
onSubscribe: async (ctx: any, msg) => {
const context = await commonContext.withRequest(ctx.extra.request)
// Return the execution args for this subscription passing through the Keystone Context
return {
schema: commonContext.graphql.schema,
operationName: msg.payload.operationName,
document: parse(msg.payload.query),
variableValues: msg.payload.variables,
contextValue: context,
}
},
},
wss
)
//wsUseServer({ schema: commonContext.graphql.schema }, wss); // For testing
//const ws = new WebSocket('ws://localhost:3000/api/graphql', 'graphql-ws'); // For testing
// Optional: Log connection events
wss.on('connection', (ws, request) => {
console.log('New WebSocket connection');
console.log('Requested Protocols:', request.headers['sec-websocket-protocol']);
console.log('Selected Protocol:', ws.protocol); // Log the subprotocol selected by the server
ws.on('close', (error) => {
console.error('WebSocket error:', error);
});
ws.on('message', (message) => {
console.log('Received message:', message);
});
ws.on('error', (error) => {
console.error('WebSocket error:', error);
});
ws.onerror = (evt) => {
console.log('Message received:', evt);
};
});
// Send the time every second as an interval example of pub/sub
/*setInterval(() => {
console.log('TIME', Date.now())
pubSub.publish('TIME', {
time: {
iso: new Date().toISOString(),
},
})
}, 1000)*/
}
我删除了 wsUseServer (Graphql 订阅)并保持连接。但我想要的是使用订阅。 SetInterval(上面注释掉)在连接时起作用。
我将 [handleProtocols] 添加到 websocketserver(上面已注释掉)。没解决。
在开发和生产环境中测试了两个 wscat 条目,两者的行为是相同的。
我期望能够维持与 graphql-ws wsUseServer 的 websocket 连接,以便能够激活订阅。
谢谢。
找到了解决办法。 首先,wscat 不是测试此功能的正确工具。 其次,正确的协议不是 graphql-ws,而是 graphql-transport-ws。无需指定,因为它是自动处理的。
我在 Apollo Playground 上测试了订阅,他们显示正在监听,但没有响应。
问题在于 Pubsub 的实施方式。我在每个必须使用它的文件中调用它,这会产生冲突。它必须在单个文件中启动,然后导出到想要使用它的每个文件。