实战指南:从 @Subscription 装饰器到自定义 PubSub 系统)
后端GraphQLAPI设计【免费下载链接】type-graphqlCreate GraphQL schema and resolvers with TypeScript, using classes and decorators!项目地址https://gitcode.com/gh_mirrors/ty/type-graphql点击查看免费下载TypeGraphQL 基于类与装饰器构建 GraphQL schema除了常规的 Query 与 Mutation 外还内置了对 GraphQL 第三种操作类型——订阅Subscription的完整支持。本文以官方 v0.17.4 文档为核心结合当前仓库中的 Subscription 装饰器实现、PubSub 接口定义 以及 simple-subscriptions 示例 与 redis-subscriptions 示例系统讲解订阅的创建、主题触发、过滤、自定义 PubSub 以及订阅服务器的搭建帮助你掌握一套可落地的实时推送方案。为什么需要订阅GraphQL 的第三种操作GraphQL 用 Query 完成读取、用 Mutation 完成写入。但在大量真实场景中客户端还希望当服务端数据发生变化时能主动收到推送而不是反复轮询。订阅Subscription正是为此而生客户端与服务端之间建立一条长期连接服务端在数据变更时把更新推送给相关客户端。TypeGraphQL 对订阅提供了开箱即用的支持。在 v0.17.4 版本中它基于 Apollo 出品的graphql-subscriptions包订阅的核心机制是一个pubsub发布/订阅系统——发布方把事件与载荷payload发送到某个主题topic订阅方监听该主题并收到回调。仓库中的PubSub接口 明确定义了这个协议publish(routingKey, ...args)负责发布subscribe(routingKey, dynamicId?)返回一个AsyncIterable。创建订阅Subscription() 装饰器订阅解析器与 Query/Mutation 解析器 类似但稍复杂。首先像往常一样定义一个普通类方法用Subscription()装饰器标注class SampleResolver { // ... Subscription() newNotification(): Notification { // ... } }从源码看Subscription.ts 会收集订阅处理器元数据其中包含topics、topicId、filter、subscribe等选项并抛出MissingSubscriptionTopicsError当topics是空数组时。指定订阅主题topics必须提供要订阅的主题支持三种形式也可以使用 TypeScript 枚举增强类型安全class SampleResolver { // ... Subscription({ topics: NOTIFICATIONS, // 单个主题字符串 topics: [NOTIFICATIONS, ERRORS], // 或者主题数组 topics: ({ args, payload, context }) args.topic, // 或根据订阅参数动态生成主题的函数 }) newNotification(): Notification { // ... } }动态主题函数接收{ args, payload, context }可根据订阅时传入的参数如args.topic决定监听哪个主题。该模式在示例中也有体现notification.resolver.ts 的subscribeToTopicFromArg用topics: ({ args }) args.topic实现动态订阅。用 filter 过滤事件filter选项决定哪些主题事件触发订阅函数应返回boolean或Promisebooleanclass SampleResolver { // ... Subscription({ topics: NOTIFICATIONS, filter: ({ payload, args }) args.priorities.includes(payload.priority), }) newNotification(): Notification { // ... } }示例仓库中notification.resolver.ts 的subscriptionWithFilter用payload.id % 2 0过滤偶数 ID 的通知subscriptionWithMultipleTopics则展示了监听多个主题并同样过滤的用法。自定义 subscribe 逻辑subscribe选项允许自定义订阅逻辑例如接入 Prisma 的订阅功能。它应是一个返回AsyncIterator的函数class SampleResolver { // ... Subscription({ subscribe: ({ args, context }) { return context.prisma.$subscribe.users({ mutation_in: [args.mutationType] }); }, }) newNotification(): Notification { // ... } }注意subscribe选项不能与topics、filter混用。如果仍需过滤可以使用graphql-subscriptions包提供的withFilter函数。用 Root() 接收载荷并转换返回形状订阅解析器通过Root()接收来自 pubsub 主题的载荷并可将其转换为返回形状class SampleResolver { // ... Subscription({ topics: NOTIFICATIONS, filter: ({ payload, args }) args.priorities.includes(payload.priority), }) newNotification( Root() notificationPayload: NotificationPayload, Args() args: NewNotificationsArgs, ): Notification { return { ...notificationPayload, date: new Date(), }; } }示例中 notification.resolver.ts 的normalSubscription即从Root()解构出id、message再拼装成带date的Notification返回recipe.resolver.ts 也展示了从Root()载荷重建Comment类型的过程。触发订阅主题PubSub() 注入发布器创建了订阅后关键在于如何触发主题。主题可由数据库等外部源触发也常在 Mutation 中触发——比如修改了客户端关心的资源时。假设有这样一个新增评论的 Mutationclass SampleResolver { // ... Mutation(returns Boolean) async addNewComment(Arg(comment) input: CommentInput) { const comment this.commentsService.createNew(input); await this.commentsRepository.save(comment); return true; } }使用PubSub()装饰器把pubsub注入方法参数即可触发主题并向所有订阅者发送载荷class SampleResolver { // ... Mutation(returns Boolean) async addNewComment(Arg(comment) input: CommentInput, PubSub() pubSub: PubSubEngine) { const comment this.commentsService.createNew(input); await this.commentsRepository.save(comment); // 在这里触发订阅主题 const payload: NotificationPayload { message: input.content }; await pubSub.publish(NOTIFICATIONS, payload); return true; } }为了便于测试mock/stub还可以只注入绑定到指定主题的publish方法使用PubSub(TOPIC_NAME)装饰器配合PublisherTPayload类型class SampleResolver { // ... Mutation(returns Boolean) async addNewComment( Arg(comment) input: CommentInput, PubSub(NOTIFICATIONS) publish: PublisherNotificationPayload, ) { const comment this.commentsService.createNew(input); await this.commentsRepository.save(comment); // 触发订阅主题 await publish({ message: input.content }); return true; } }这样所有挂在NOTIFICATIONS主题上的订阅在addNewCommentMutation 执行时都会被触发。在仓库示例中recipe.resolver.ts 的addNewComment正是先保存评论再通过pubSub.publish(Topic.NEW_COMMENT, {...})发布新评论载荷。使用自定义 PubSub 系统面向生产环境的扩展默认情况下TypeGraphQL 使用graphql-subscriptions中基于 EventEmitter 的简单PubSub。它有一个明显缺陷仅在单个 Node.js 进程实例下才正确工作。为了更好的可扩展性应使用外部存储如 Redis支撑的 PubSub 实现例如graphql-redis-subscriptions包。只需按包文档创建 PubSub 实例再传入buildSchema选项const myRedisPubSub getConfiguredRedisPubSub(); const schema await buildSchema({ resolvers: [__dirname /**/*.resolver.ts], pubSub: myRedisPubSub, });当前仓库的 redis-subscriptions 示例 展示了生产级做法通过createPubSub搭配graphql-yoga/redis-event-target创建基于 Redis 的分布式 pubsub并使用ioredis的retryStrategy配置重连策略index.ts 中通过buildSchema({ resolvers: [RecipeResolver], pubSub })注入该实例。需要说明的是v0.17.4 文档以graphql-redis-subscriptions为例当前仓库示例采用的是 Yoga 生态的 Redis 事件目标实现——两者都遵循publish/subscribe协议且启动 Redis 示例前必须有运行中的 Redis 实例并可能需要按环境变量如REDIS_URL调整连接参数。创建订阅服务器WebSocket 传输层引导指南 及之前的示例都使用apollo-server为 GraphQL API 创建 HTTP 端点。要启用订阅无需手动实现不受 HTTP 约束、可做推送式通信WebSocket的传输层——apollo-server内置了基于 WebSocket 的订阅支持开箱即用无需改动引导配置。当然如果需要也可以显式提供subscriptions配置// 创建 GraphQL 服务器 const server new ApolloServer({ schema, subscriptions: { path: /subscriptions, // 其他选项与钩子如 onConnect }, });完成现在在/subscriptions路径下我们同时拥有了 GraphQL 订阅服务器和普通 HTTP GraphQL 服务器。仓库的 simple-subscriptions/index.ts 展示了当前版本更主流的做法用graphql-yoga的createYoga({ schema })创建支持订阅的服务器再交给 Node 原生http模块监听 4000 端口。示例与进一步阅读查看订阅如何工作的 simple-subscriptions 示例包含普通订阅、过滤订阅、多主题订阅、动态主题、动态主题 ID 等完整演示其生成的 schema.graphql 中可看到type Subscription对应的字段定义。生产环境建议使用更可扩展的 Redis 型 pubsub 系统参考 redis-subscriptions 示例。注意启动该示例需要运行中的 Redis 实例并可能需修改代码提供连接参数示例通过REDIS_URL环境变量读取。相关 API 细节可继续阅读 Subscription 装饰器源码、PubSub 接口 与 subscriptions 测试。从创建订阅、过滤事件、触发主题到更换可扩展的 Redis pubsub 与搭建支持 WebSocket 的订阅服务器本文覆盖了 TypeGraphQL 订阅的完整链路。接下来你可以在自己的解析器中按需组合这些能力构建出稳健的实时数据推送功能。赞分享后端GraphQLAPI设计【免费下载链接】type-graphqlCreate GraphQL schema and resolvers with TypeScript, using classes and decorators!项目地址https://gitcode.com/gh_mirrors/ty/type-graphql点击查看免费下载相关推荐OBS多路RTMP推流插件深度解析与实践指南OBS多路RTMP推流插件深度解析与实践指南 在当今多平台直播成为主流的背景下内容创作者面临着同时向多个平台推送直播流的复杂需求。传统的单路推流方式不仅效率低后端GraphQLAPI设计TypeGraphQL 订阅Subscriptions完全指南从 Subscription 装饰器到生产级 PubSub 架构TypeGraphQL 订阅Subscriptions完全指南从 Subscription 装饰器到生产级 PubSub 架构 导读 本文以 Type后端GraphQLAPI设计TypeGraphQL 订阅Subscriptions完整实战指南从装饰器到自定义 PubSub 与 WebSocket 服务端TypeGraphQL 订阅Subscriptions完整实战指南从装饰器到自定义 PubSub 与 WebSocket 服务端 TypeGraphQL后端GraphQLAPI设计创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考