Search code examples
javascriptexpressgraphqlapolloapollo-server

How to close socket connection for GraphQL subscription in Apollo


I have GraphQL Subscriptions on my Apollo server that I want to close after the user logs out. The initial question is whether we should close this (socket) connections on the client side or in the backend.

On the front-end, I am using Angular with Apollo Client and I handle GraphQL subscriptions by extending the Subscription class from apollo-angular. I am able to close the subscription channels with a typical takeUntil rxjs implementation:

this.userSubscription
  .subscribe()
  .pipe(takeUntil(this.subscriptionDestroyed$))
  .subscribe(
    ({ data }) => {
      // logic goes here
    },
    (error) => {
      // error handling
   }
);
  

However, this does not close the websocket on the server, which If I'm right, will result in a subscription memory leak.

The way the Apollo Server (and express) is set up for subscriptions is as follows:

const server = new ApolloServer({
  typeDefs,
  resolvers,
  subscriptions: {
    onConnect: (connectionParams, webSocket, context) => {
      console.log('on connect');
      const payload = getAuthPayload(connectionParams.accessToken);
      if (payload instanceof Error) {
        webSocket.close();
      }
      return { user: payload };
    },
    onDisconnect: (webSocket, context) => {
      console.log('on Disconnect');
    }
  },
  context: ({ req, res, connection }) => {
    if (connection) {
      // set up context for subscriptions...
    } else {
      // set up context for Queries, Mutations...
    }

When the client registers a new GraphQL subscription, I always get to see console.log('on connect'); on the server logs, but I never see console.log('on Disconnect'); unless I close the front-end application.

I haven't seen any example on how to close the websocket for subscriptions with Apollo. I mainly want to do this to complete a Logout implementation.

Am I missing something here? Thanks in advance!


Solution

  • I based my solution based on this post

    Essentially, the way we created the Subscription with sockets was using subscriptions-transport-ws

    export const webSocketClient: SubscriptionClient = new 
    SubscriptionClient(
      `${environment.WS_BASE_URL}/graphql`,
      {
        reconnect: true,
        lazy: true,
        inactivityTimeout: 3000,
        connectionParams: () => ({
          params: getParams()
        })
      }
    );
    

    As specified in the question, I wanted to unsubscribe all channels and close the subscription socket connection before logout of the user. We do this by using the webSocketClient SubscriptionClient in the logout function and call:

    webSocketClient.unsubscribeAll();
    webSocketClient.close();