使用graphql-ws订阅无法获取初始Todo数据求助
GraphQL-Yoga + graphql-ws 订阅无法获取初始Todo列表问题
刚接触graphql-ws和graphql-yoga服务器,若问题浅显还请见谅。查阅了graphql-ws文档,其中将schema作为参数,但文档里的schema定义缺少参考。使用addTodo添加新待办项后,能显示两个Todo,但在Yoga Graphiql explorer中订阅时无法获取初始Todo列表。按预期订阅后应立即显示初始Todo项,认为是schema定义中遗漏了某些内容导致该问题,请问是否有人遇到过类似问题并解决?遗漏了什么?
使用的库
- 后端:graphql-yoga、ws、graphql-ws
- 前端:solid-js、wonka
Schema中声明的初始Todo项
{ "id": "1", "title": "Learn GraphQL + Solidjs", "completed": false }
截图

代码片段
Schema定义
import { createPubSub } from 'graphql-yoga'; import { Todo } from "./types"; let todos = [ { id: "1", title: "Learn GraphQL + Solidjs", completed: false } ]; // channel const TODOS_CHANNEL = "TODOS_CHANNEL"; // pubsub const pubSub = createPubSub(); const publishToChannel = (data: any) => pubSub.publish(TODOS_CHANNEL, data); // Type def const typeDefs = [` type Todo { id: ID! title: String! completed: Boolean! } type Query { getTodos: [Todo]! } type Mutation { addTodo(title: String!): Todo! } type Subscription { todos: [Todo!] } `]; // Resolvers const resolvers = { Query: { getTodos: () => todos }, Mutation: { addTodo: (_: unknown, { title }: Todo) => { const newTodo = { id: "" + (todos.length + 1), title, completed: false }; todos.push(newTodo); publishToChannel({ todos }); return newTodo; }, Subscription: { todos: { subscribe: () => { const res = pubSub.subscribe(TODOS_CHANNEL); publishToChannel({ todos }); return res; } }, }, }; export const schema = { resolvers, typeDefs };
Server后端
import { createServer } from "graphql-yoga"; import { WebSocketServer } from "ws"; import { useServer } from "graphql-ws/lib/use/ws"; import { schema } from "./src/schema"; import { execute, ExecutionArgs, subscribe } from "graphql"; async function main() { const yogaApp = createServer({ schema, graphiql: { subscriptionsProtocol: 'WS', // use WebSockets instead of SSE }, }); const server = await yogaApp.start(); const wsServer = new WebSocketServer({ server, path: yogaApp.getAddressInfo().endpoint }); type EnvelopedExecutionArgs = ExecutionArgs & { rootValue: { execute: typeof execute; subscribe: typeof subscribe; }; }; useServer( { execute: (args: any) => (args as EnvelopedExecutionArgs).rootValue.execute(args), subscribe: (args: any) => (args as EnvelopedExecutionArgs).rootValue.subscribe(args), onSubscribe: async (ctx, msg) => { const { schema, execute, subscribe, contextFactory, parse, validate } = yogaApp.getEnveloped(ctx); const args: EnvelopedExecutionArgs = { schema, operationName: msg.payload.operationName, document: parse(msg.payload.query), variableValues: msg.payload.variables, contextValue: await contextFactory(), rootValue: { execute, subscribe, }, }; const errors = validate(args.schema, args.document); if (errors.length) return errors; return args; }, }, wsServer, ); } main().catch((e) => { console.error(e); process.exit(1); });
问题原因及修复方案
- 语法错误导致订阅Resolver未被正确注册
你的Schema定义代码中,Mutation对象没有正确闭合,导致Subscription被错误嵌套在Mutation内部,GraphQL无法识别到订阅的Resolver,这是核心问题。
修复方法:在Mutation的addTodo resolver后添加闭合的}和逗号:
// Resolvers const resolvers = { Query: { getTodos: () => todos }, Mutation: { addTodo: (_: unknown, { title }: Todo) => { const newTodo = { id: "" + (todos.length + 1), title, completed: false }; todos.push(newTodo); publishToChannel({ todos }); return newTodo; }, }, // 新增该行,闭合Mutation对象 Subscription: { todos: { subscribe: () => { const res = pubSub.subscribe(TODOS_CHANNEL); publishToChannel({ todos }); return res; } }, }, };
- 订阅字段类型优化(可选)
你的Subscription类型定义中,todos: [Todo!]可以改为[Todo!]!,明确表示返回的Todo列表非空,更符合业务逻辑:
type Subscription { todos: [Todo!]! }
- 验证发布的数据结构
你当前发布的{ todos }结构是正确的,因为订阅字段名为todos,GraphQL会自动从payload中提取对应字段的值,无需额外修改。
修复后重启服务器,订阅todos时就能立即收到初始的Todo列表,后续添加新Todo时也能实时更新。
内容的提问来源于stack exchange,提问作者adityajava6
相关产品推荐
相关产品推荐

