The below setup seems to work fine on Safari, Mozilla.
At times it works fine on some Google Chrome profiles as well. For example if I open multiple profiles of chrome then it works on one and doesn’t in another. I have checked that none oof my extensions block it and no firewall has been imposed.
Further the prolem happens not only for my client but also for GraphQL IDE hence I believe that the client setup may not matter.
Tools and Infra
Client: React
Server: FastApi, StrawberryApi
Wildcard SSL for our site using AWS Certificate Manager
Nginx as reverse proxy from EC2 to myserver.site.com/api/graphql
Client Setup:
REACT_APP_WEBSOCKET_URL
being myserver.site.com or it’s IP
import { ApolloClient, InMemoryCache, HttpLink, split } from '@apollo/client'
import { getMainDefinition } from '@apollo/client/utilities'
import { GraphQLWsLink } from "@apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";
const protocol = process.env.REACT_APP_SSL === 'true' ? 'https' : 'http'
const wsProtocol = process.env.REACT_APP_SSL === 'true' ? 'wss' : 'ws'
const httpLink = new HttpLink({
uri: `${protocol}://${process.env.REACT_APP_WEBSOCKET_URL}/api/graphql`,
})
const wsLink = new GraphQLWsLink(
createClient({
url: `${wsProtocol}://${process.env.REACT_APP_WEBSOCKET_URL}/api/graphql`,
options: {
reconnect: true,
lazy: true,
minTimeout: 10000,
},
}),
);
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
wsLink,
httpLink
)
const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
})
export default client
Server Setup:
import strawberry
from fastapi import FastAPI
from strawberry.fastapi import GraphQLRouter
from typing import AsyncGenerator
@strawberry.type
class Query:
hello: str = "Hello world"
@strawberry.type
class Subscription:
@strawberry.subscription
async def count(self) -> AsyncGenerator[int, None]:
for i in range(1, 11):
yield i
schema = strawberry.Schema(query=Query, subscription=Subscription)
graphql_app = GraphQLRouter(schema)
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Allows all origins
allow_credentials=True,
allow_methods=["*"], # Allows all methods
allow_headers=["*"], # Allows all headers
)
app.include_router(graphql_app, prefix="/api/graphql")
Nginx Configuration
server {
listen 80;
server_name myserver.site.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Error received on browser console:
WebSocket connection to 'wss:///myserver.site.com/api/graphql' failed: WebSocket opening handshake was canceled
Subscription error: Error: Socket closed
at Object.error (index.ts:69:17)
at client.mjs:424:18
In Network Tab:
Message: WebSocket opening handshake was canceled
No Request Headers as it shows Provisional headers are shown
Additional Context
I tried below nginx config as well, but no luck
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream websockets {
server 127.0.0.1:8000;
}
server {
listen 80;
listen 443;
server_name myserver.site.com;
location /api/graphql {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
# Additional headers for WebSocket
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts for long-lived WebSocket connections
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
proxy_connect_timeout 86400s;
}
}
I tried to make listen 443;
-> listen 443 ssl;
But I didn’t had certificate configurations to add here. And the wildcard SSL is already applied thorgh AWS.
amogh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.