Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subscriptions not working - code: 4406, reason: "Subprotocol not acceptable" #9334

Open
jj-matos opened this issue Sep 17, 2024 · 0 comments

Comments

@jj-matos
Copy link

Followed the documentation: https://keystonejs.com/docs/config/config#extend-http-server

and the keytone github example for websocket file: https://github.com/keystonejs/keystone/blob/main/examples/extend-graphql-subscriptions/websocket.ts

The created subscriptions appear in the Apollo playground schema.

The websocket connection works as long as I comment out graphql-ws from the code. When I add wsUseServer coming from graphql-ws it gives the following errors on these two tries:

wscat -c ws://localhost:3000/api/graphql >
Connected (press CTRL+C to quit)
Disconnected (code: 4406, reason: "Subprotocol not acceptable")

wscat -c ws://localhost:3000/api/graphql -s graphql-ws
error: Server sent no subprotocol

My websocket.ts code which is the same as the example, excepting logs and commented test lines for debugging:

import type http from 'http'
import { useServer as wsUseServer } from 'graphql-ws/lib/use/ws'
import { WebSocketServer } from 'ws'
import { PubSub } from 'graphql-subscriptions'
import { parse } from 'graphql'
import { type Context } from '.keystone/types'

// Setup pubsub as a Global variable in dev so it survives Hot Reloads.
declare global {
  let graphqlSubscriptionPubSub: PubSub
}

// The 'graphql-subscriptions' pubsub library is not recommended for production use, but can be useful as an example
//   for details see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#the-pubsub-class
export const pubSub = global.graphqlSubscriptionPubSub || new PubSub()
globalThis.graphqlSubscriptionPubSub = pubSub

export function extendHttpServer (
  httpServer: http.Server,
  commonContext: Context,
) {
  console.log('HTTP Server:', httpServer);  
  console.log(commonContext.graphql.schema)

  // Setup WebSocket server using 'ws'
  const wss = new WebSocketServer({
    server: httpServer,
    path: '/api/graphql',
    /*handleProtocols: (protocols) => {
      return Array.from(protocols).includes('graphql-ws') ? 'graphql-ws' : null;
    }*/
  })

  // Setup the WebSocket to handle GraphQL subscriptions using 'graphql-ws'
  wsUseServer(
    {
      schema: commonContext.graphql.schema,
      // run these onSubscribe functions as needed or remove them if you don't need them
      onSubscribe: async (ctx: any, msg) => {
        const context = await commonContext.withRequest(ctx.extra.request)
        // Return the execution args for this subscription passing through the Keystone Context
        return {
          schema: commonContext.graphql.schema,
          operationName: msg.payload.operationName,
          document: parse(msg.payload.query),
          variableValues: msg.payload.variables,
          contextValue: context,
        }
      },
    },
    wss
  )

  //wsUseServer({ schema: commonContext.graphql.schema }, wss); // For testing
  //const ws = new WebSocket('ws://localhost:3000/api/graphql', 'graphql-ws'); // For testing

  // Optional: Log connection events
  wss.on('connection', (ws, request) => {
    console.log('New WebSocket connection');
    console.log('Requested Protocols:', request.headers['sec-websocket-protocol']);
    console.log('Selected Protocol:', ws.protocol); // Log the subprotocol selected by the server    

    ws.on('close', (error) => {
      console.error('WebSocket error:', error);
    });    
    
    ws.on('message', (message) => {
      console.log('Received message:', message);
    });

    ws.on('error', (error) => {
      console.error('WebSocket error:', error);
    });
    ws.onerror = (evt) => {
  console.log('Message received:', evt);
};
  });

  // Send the time every second as an interval example of pub/sub
  /*setInterval(() => {
    console.log('TIME', Date.now())
    pubSub.publish('TIME', {
      time: {
        iso: new Date().toISOString(),
      },
    })
  }, 1000)*/
}

Removed wsUseServer (Graphql Subscriptions) and the connection is maintained. But what I want is to use the subscriptions. SetInterval (commented out above) is working upon connection.

Added [handleProtocols] to the websocketserver (commented out above). Didn't solve.

Tested both wscat entries in development and production environment and the behavior is the same on both.

What I expect is to be able to maintain the websocket connection with the graphql-ws wsUseServer, to be able to activate subscriptions.

Also added a question on Stackoverflow:
https://stackoverflow.com/staging-ground/78994358

Put a comment on a related closed issue, but thought maybe creating this as a new issue would be best (#2857)

Looking forward to your feedback. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant