You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apollo Server中subscriptionsUrl返回undefined及GraphQL订阅无法在Playground运行的问题求助

解决Apollo Server 3.x中订阅subscriptionsUrl undefined及协议不支持问题

我之前也碰到过一模一样的问题,Apollo Server 3.x(包括你在用的3.10.3版本)已经移除了默认内置的WebSocket订阅支持,这就是为什么你直接调用server.listen()时subscriptionsUrl返回undefined,并且客户端会提示协议不支持的核心原因。

要搞定这个问题,你需要手动创建HTTP服务器并配置WebSocket订阅服务,具体步骤如下:

1. 安装必要依赖

首先安装订阅所需的WebSocket库(优先推荐graphql-ws,因为subscriptions-transport-ws已进入维护模式):

npm install graphql-ws ws subscriptions-transport-ws

2. 修改服务器代码

你需要放弃直接使用server.listen(),转而用Express创建HTTP服务器,然后挂载Apollo Server并启动WebSocket服务。修改后的完整代码如下:

import * as dotenv from 'dotenv'
dotenv.config()
import './mongo.js'
import express from 'express'
import { createServer } from 'http'
import { ApolloServer, AuthenticationError, gql, UserInputError } from 'apollo-server-express'
import { PubSub } from 'graphql-subscriptions'
import { WebSocketServer } from 'ws'
import { createServer as createGraphQLWsServer } from 'graphql-ws/lib/use/ws'
import { execute, subscribe } from 'graphql'
import Person from './models/person.js'
import User from './models/user.js'
import jwt from 'jsonwebtoken'

const { JWT_SECRET } = process.env
const SUBS_EVENTS = { PERSON_ADDED: 'PERSON_ADDED' }
const pubsub = new PubSub()

const typeDefs = gql`...` // 保留你原有的typeDefs定义
const resolvers = {...} // 保留你原有的resolvers逻辑

// 1. 创建Express实例和HTTP服务器
const app = express()
const httpServer = createServer(app)

// 2. 创建Apollo Server实例,同时处理HTTP和WebSocket的上下文
const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: async ({ req, connection }) => {
    // 处理HTTP请求的认证逻辑
    if (req) {
      const auth = req ? req.headers.authorization : null
      if (auth && auth.toLowerCase().startsWith(`bearer `)) {
        const token = auth.substring(7)
        const { id } = jwt.verify(token, JWT_SECRET)
        const currentUser = await User.findById(id).populate('friends')
        return { currentUser }
      }
    }
    // 处理WebSocket订阅的认证逻辑(从客户端连接参数中获取token)
    if (connection) {
      const auth = connection.context.authorization || ''
      if (auth.toLowerCase().startsWith('bearer ')) {
        const token = auth.substring(7)
        const { id } = jwt.verify(token, JWT_SECRET)
        const currentUser = await User.findById(id).populate('friends')
        return { currentUser }
      }
    }
    return {}
  }
})

// 3. 启动Apollo Server并挂载到Express
await server.start()
server.applyMiddleware({ app })

// 4. 配置graphql-ws的WebSocket服务器
const wsServer = new WebSocketServer({
  server: httpServer,
  path: server.graphqlPath, // 和GraphQL HTTP端点使用同一路径,比如'/graphql'
})

createGraphQLWsServer({
  schema: server.schema,
  execute,
  subscribe,
  // 订阅连接建立时,提取客户端传递的认证参数
  onConnect: async (ctx) => {
    return {
      authorization: ctx.connectionParams.authorization
    }
  },
}, wsServer)

// 5. 启动HTTP服务器
const PORT = process.env.PORT || 4000
httpServer.listen(PORT, () => {
  console.log(`Server ready at http://localhost:${PORT}${server.graphqlPath}`)
  console.log(`Subscriptions ready at ws://localhost:${PORT}${server.graphqlPath}`)
})

3. 关键说明

  • 为什么原代码不生效?:Apollo Server 3.x移除了内置的WebSocket支持,必须手动配置WebSocket服务器,所以直接调用server.listen()不会启动订阅服务,自然没有subscriptionsUrl返回。
  • 订阅认证处理:WebSocket订阅无法通过req对象获取认证信息,需要从客户端发送的connectionParams中提取token,所以要在onConnect回调中处理并传递到上下文。
  • Apollo Playground适配:Playground会自动检测同路径的WebSocket端点,只要你配置了正确的ws地址,就能正常连接并测试订阅。

可选:兼容旧版subscriptions-transport-ws协议

如果你的客户端还在使用旧的subscriptions-transport-ws协议,可以在启动HTTP服务器之前添加以下代码,实现双协议支持:

import { SubscriptionServer } from 'subscriptions-transport-ws'

SubscriptionServer.create(
  {
    schema: server.schema,
    execute,
    subscribe,
    onConnect: async (connectionParams) => {
      return {
        authorization: connectionParams.authorization
      }
    },
  },
  {
    server: httpServer,
    path: server.graphqlPath,
  }
)

这样就能同时支持两种订阅协议,适配不同版本的客户端。

内容的提问来源于stack exchange,提问作者heyAlanDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 13:23:13