nitrojs / nitrojs/nitro

feat: expose WebSocket serverOptions for crossws adapter

Open
#3,925 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug v3
Dominant language
TypeScript
Stars
11.2k
Forks
899
Avg merge
2d 24m
Merged PRs (30d)
40

Description

Problem

When using WebSocket with the graphql-ws protocol (for GraphQL subscriptions), browsers reject the connection with:

WebSocket connection failed: Error during WebSocket handshake: 
Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received

This happens because crossws's node adapter has handleProtocols: () => false hardcoded:

// crossws/dist/adapters/node.mjs
const wss = options.wss || new _WebSocketServer({
  noServer: true,
  handleProtocols: () => false,  // ← Problem
  ...options.serverOptions       // ← Solution exists but not exposed
});

The ws library uses handleProtocols to negotiate WebSocket subprotocols. When it returns false, browsers reject the handshake even if we manually add Sec-WebSocket-Protocol header in the upgrade hook.

Current Workaround

Adding the header manually in the upgrade hook works for Node.js clients but not for browsers:

defineWebSocketHandler({
  upgrade(request) {
    const protocol = request.headers.get('sec-websocket-protocol')
    if (protocol?.includes('graphql-transport-ws')) {
      return {
        headers: { 'Sec-WebSocket-Protocol': 'graphql-transport-ws' }
      }
    }
  }
})

Proposed Solution

Expose serverOptions in Nitro's WebSocket configuration so users can customize the crossws adapter:

// nitro.config.ts
export default defineNitroConfig({
  features: {
    websocket: true
  },
  websocket: {
    serverOptions: {
      handleProtocols: (protocols) => {
        if (protocols.has('graphql-transport-ws')) {
          return 'graphql-transport-ws'
        }
        return false
      }
    }
  }
})

Or at minimum, allow passing serverOptions to the crossws adapter in the runtime presets.

Use Case

This is needed for GraphQL subscriptions over WebSocket using the graphql-ws protocol, which is the standard for:

  • GraphQL Yoga
  • Apollo Server
  • Any GraphQL server implementing the graphql-ws spec

Environment

  • Nitro: 3.0.1-alpha.1
  • crossws: 0.4.1
  • Node.js: 24.x

Related

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing the websocket configuration from nitro.config.ts into the runtime presets and the crossws adapter at src/adapters/node.ts. Verify how serverOptions can reach the adapter, then check the GraphQL subprotocol handshake; done means users can configure handleProtocols without the manual upgrade-hook workaround.

Written by the indexing model from the issue text.

Assessment

Tech stack
graphql, typescript
Domain
api, backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.