如何在@apollo/client与apollo-server-express的GraphQL订阅中传递Header
解决Apollo Client WebSocketLink传递自定义Header到Apollo Server订阅的问题
客户端配置(@apollo/client)
需要在创建WebSocketLink时,通过options.connectionParams配置自定义Header,这部分参数会在WebSocket握手阶段发送给服务端:
import { WebSocketLink } from '@apollo/client/link/ws'; import { split, HttpLink } from '@apollo/client'; import { getMainDefinition } from '@apollo/client/utilities'; import { InMemoryCache, ApolloClient } from '@apollo/client'; // 创建WebSocket链接 const wsLink = new WebSocketLink({ uri: 'ws://your-server-url/graphql', options: { reconnect: true, // 配置握手时传递的参数 connectionParams: { headers: { 'X-Auth-Token': 'my-auth-token', }, }, // 如果token是动态获取的,可改为函数形式 // connectionParams: () => ({ // headers: { // 'X-Auth-Token': localStorage.getItem('auth-token'), // }, // }), }, }); // 创建HTTP链接(用于查询/突变操作) const httpLink = new HttpLink({ uri: 'http://your-server-url/graphql', }); // 拆分链接:根据操作类型自动选择HTTP或WebSocket const splitLink = split( ({ query }) => { const definition = getMainDefinition(query); return ( definition.kind === 'OperationDefinition' && definition.operation === 'subscription' ); }, wsLink, httpLink, ); // 初始化Apollo Client const client = new ApolloClient({ link: splitLink, cache: new InMemoryCache(), });
之后调用client.subscribe时无需额外设置context,握手阶段已经传递了Header信息:
const sub = await client.subscribe({ query: MY_SUBSCRIPTION, variables: { // 你的订阅变量 }, });
服务端配置(apollo-server-express)
需要在ApolloServer的subscriptions选项中添加onConnect回调,将客户端传递的connectionParams注入到上下文,这样订阅Resolver就能获取到Header:
const { ApolloServer } = require('apollo-server-express'); const express = require('express'); const app = express(); const server = new ApolloServer({ typeDefs, // 你的GraphQL类型定义 resolvers, // 你的Resolver subscriptions: { onConnect: (connectionParams) => { // 将客户端传递的headers存入上下文 return { headers: connectionParams.headers, }; }, }, }); // 应用中间件并安装订阅处理器 server.applyMiddleware({ app }); server.installSubscriptionHandlers(app); app.listen({ port: 4000 }, () => { console.log(`Server ready at http://localhost:4000${server.graphqlPath}`); console.log(`Subscriptions ready at ws://localhost:4000${server.subscriptionsPath}`); });
此时你的订阅Resolver就能正常获取到Header:
const resolvers = { Subscription: { mySubscription: { subscribe: (_, args, context) => { console.log(context.connection.context.headers); // 现在会输出{ 'X-Auth-Token': 'my-auth-token' } // 你的订阅逻辑 }, }, }, };
为什么原方法无效?
client.subscribe中的context是针对单个订阅请求的上下文,而WebSocket是持久连接,自定义Header需要在连接建立的握手阶段传递,而非单个订阅请求时。因此必须通过WebSocketLink的connectionParams配置,才能让Header被服务端获取到。
内容的提问来源于stack exchange,提问作者Moritz Roessler
相关产品推荐
相关产品推荐

