Apollo GraphQL服务器;设置上下文以处理由已触发的订阅触发的请求

时间:2017-08-08 17:29:29

标签: graphql apollo-server graphql-subscriptions

我了解如何在创建GraphQL服务器时设置上下文对象,例如

const app = express();
app.use(GRAPHQL_URL, graphqlExpress({
            schema,
            context: {
                foo: 'bar'
            },
    }));

以便在处理传入请求时将上下文对象传递给我的解析器。

但是,当订阅者触发解析器时,我没有看到此上下文对象(即客户端订阅GraphQL订阅,并定义订阅触发时要发送给它们的数据的形状);在这种情况下,上下文似乎是一个空对象。

在PubSub.publish()调用之后调用解析器时,有没有办法确保我的上下文对象设置正确?

2 个答案:

答案 0 :(得分:0)

我猜你正在使用包subscription-transport-ws。在这种情况下,可以在不同的执行步骤中添加上下文值。 见API。两种可能的情况

  1. 如果您有某种身份验证。您可以在onConnect执行步骤中在上下文中添加查看器。这是在第一次连接到websocket时完成的,并且不会改变,直到连接关闭并再次打开。请参阅example

  2. 如果您想更动态地添加上下文,可以在执行步骤之前添加一种中间件。它可能如下所示:

  3. 
    
    const middleware = (args) => new Promise((resolve, reject) => {
      const [schema, document, root, context, variables, operation] = args;
      context.foo = "bar"; // add something to context
      resolve(args);
    })
    
    subscriptionServer = SubscriptionServer.create({
      schema: executable.schema,
      subscribe,
      execute: (...args) => middleware(args).then(args => {
        return execute(...args);
      })
    }, {
      server: websocketServer,
      path: "/graphql",
    }, );
    
    
    

答案 1 :(得分:0)

这是我的解决方法:

  1. 您可以像这样传递上下文并为graphql subscription(WebSocket)进行身份验证:
proc getval {argc argv} {
    global val
    for {set i 0} {$i < $argc} {incr i} {
        set arg [lindex $argv $i]
        if {[string index $arg 0] eq "-"} {
            set val([string range $arg 1 end]) [lindex $argv [incr i]]
        }
    }
}
  1. 您可以在解析器中使用const server = new ApolloServer({ typeDefs, resolvers, context: contextFunction, introspection: true, subscriptions: { onConnect: ( connectionParams: IWebSocketConnectionParams, webSocket: WebSocket, connectionContext: ConnectionContext, ) => { console.log('websocket connect'); console.log('connectionParams: ', connectionParams); if (connectionParams.token) { const token: string = validateToken(connectionParams.token); const userConnector = new UserConnector<IMemoryDB>(memoryDB); let user: IUser | undefined; try { const userType: UserType = UserType[token]; user = userConnector.findUserByUserType(userType); } catch (error) { throw error; } const context: ISubscriptionContext = { // pubsub: postgresPubSub, pubsub, subscribeUser: user, userConnector, locationConnector: new LocationConnector<IMemoryDB>(memoryDB), }; return context; } throw new Error('Missing auth token!'); }, onDisconnect: (webSocket: WebSocket, connectionContext: ConnectionContext) => { console.log('websocket disconnect'); }, }, }); 方法来传递解析器的上下文参数,如下所示:
pubsub.publish
  1. 现在,我们可以在以下位置获取http请求上下文和subscription(websocket)上下文: 您的addTemplate: ( __, { templateInput }, { templateConnector, userConnector, requestingUser }: IAppContext, ): Omit<ICommonResponse, 'payload'> | undefined => { if (userConnector.isAuthrized(requestingUser)) { const commonResponse: ICommonResponse = templateConnector.add(templateInput); if (commonResponse.payload) { const payload = { data: commonResponse.payload, context: { requestingUser, }, }; templateConnector.publish(payload); } return _.omit(commonResponse, 'payload'); } }, 解析器Subscription方法如下:
subscribe
Subscription: {
    templateAdded: {
      resolve: (
        payload: ISubscriptionPayload<ITemplate, Pick<IAppContext, 'requestingUser'>>,
        args: any,
        subscriptionContext: ISubscriptionContext,
        info: any,
      ): ITemplate => {
        return payload.data;
      },
      subscribe: withFilter(templateIterator, templateFilter),
    },
  },

如您所见,现在我们从async function templateFilter( payload?: ISubscriptionPayload<ITemplate, Pick<IAppContext, 'requestingUser'>>, args?: any, subscriptionContext?: ISubscriptionContext, info?: any, ): Promise<boolean> { console.count('templateFilter'); const NOTIFY: boolean = true; const DONT_NOTIFY: boolean = false; if (!payload || !subscriptionContext) { return DONT_NOTIFY; } const { userConnector, locationConnector } = subscriptionContext; const { data: template, context } = payload; if (!subscriptionContext.subscribeUser || !context.requestingUser) { return DONT_NOTIFY; } let results: IUser[]; try { results = await Promise.all([ userConnector.findByEmail(subscriptionContext.subscribeUser.email), userConnector.findByEmail(context.requestingUser.email), ]); } catch (error) { console.error(error); return DONT_NOTIFY; } //... return true; } 和HTTP请求{{1}中获得了订阅用户(他们与graphql Web服务器建立了WebSocket连接)和HTTP请求用户(将变异发送到graphql Web服务器) }。

然后,如果subscriptionContext函数的返回值正确,则可以完成其余工作,然后WebSocket将推送消息以context订阅用户,否则不会。

templateFilter函数将被多次执行,具体取决于订阅用户的数量,这意味着它是可迭代的。现在,您将获得此功能中的每个订阅用户,并执行业务逻辑来确定是否将WebSocket消息推送到订阅用户(客户端)。

请参见github example repo

文章: