Andrew Gura
Andrew Gura

Reputation: 382

Tracking online user with GraphQL Apollo

I need to handle events "user is now online" and "user is now offline" on GraphQL Apollo Node.js server. What's the best way to do it?

My investigation: I pretty sure that I don't need to implement any heartbeat logic, because subscriptions are working on WebSockets. But I didn't find any info in their docs how to handle WebSockets events like "connecting" and "disconnecting" from the subscription... Actually I can handle those events from the outside of actual subscription:

SubscriptionServer.create({
    execute,
    subscribe,
    schema,
    onConnect = (...args) => {
        console.log('User connected')
    },
    onDisconnect = (...args) => {
        console.log('User disconnected')
    }
}, {
    server: ws,
    path: '/subscriptions'
})

But can't determine which user is connected via this socket.

My implementation: for now I made it work like that:

  1. We have express middleware for all the calls, it is pushing user object from jsonwebtoken to req object. Here I can trigger "user is now online" logic.

  2. I've created separate subscription, client subscribes on it on login and unsubscribes on logout. Since there is no unsubscribe handler, I manage to determine that filter function gets called on user disconnect without payload, so I did this approach:

    userOnlineSubscription: {
        subscribe: withFilter(
            () => pubSub.asyncIterator('userOnlineSubscription'),
                async (payload, variables) => {
                    if (!payload) {
                        // set user offline
                }
                return false
            }
        )
    }
    

As for me, the solution above is ugly. Can someone recommend the better approach?

Upvotes: 9

Views: 2646

Answers (1)

Medet Tleukabiluly
Medet Tleukabiluly

Reputation: 11930

I used this approach

onConnect (connectionParams, webSocket) {
  const userPromise = new Promise((resolve, reject) => {
    if (connectionParams.jwt) {
      jsonwebtoken.verify(
        connectionParams.jwt,
        JWT_SECRET,
        (err, decoded) => {
          if (err) {
            reject(new Error('Invalid Token'))
          }

          resolve(
            User.findOne({
              where: { id: decoded.id }
            })
          )
        }
      )
    } else {
      reject(new Error('No Token'))
    }
  })

  return userPromise.then(user => {
    if (user) {
      return { user: Promise.resolve(user) }
    }

    return Promise.reject(new Error('No User'))
  })
}

Upvotes: 2

Related Questions