cannot connect graphql-ws to gqlgen

363 Views Asked by At

I'm using gqlgen for my service and apollo client and graphql-ws for my front end and I'm trying to use a subscription and it perfectly works in my playground but when i try to connect the client to it I receive this error:

WebSocket connection to 'ws://localhost:8080/' failed:

and in my container log I receive:

unable to upgrade *http.response to websocket websocket: request origin not allowed by Upgrader.CheckOrigin: http: superfluous response.WriteHeader call from github.com/99designs/gqlgen/graphql/handler/transport.SendError (error.go:15)

here is my golang code:

if err := godotenv.Load(); err != nil {
    log.Fatal("Error loading environment variables file")
}

port := helpers.Env("PORT")
if port == "" {
    port = defaultPort
}

router := chi.NewRouter()

router.Use(cors.New(cors.Options{
    AllowedOrigins:   strings.Split(helpers.Env("ALLOWED_ORIGINS"), ","),
    AllowCredentials: true,
    Debug:            helpers.Env("DEBUG") == "true",
    AllowedHeaders:   []string{"*"},
}).Handler)

srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &resolvers.Resolver{}}))


srv.AddTransport(transport.POST{})
upgrader := &transport.Websocket{
    Upgrader: websocket.Upgrader{
        HandshakeTimeout: 1 * time.Minute,
        CheckOrigin: func(r *http.Request) bool {
            return true
        },
        ReadBufferSize:  1024,
        WriteBufferSize: 1024,
    },
    KeepAlivePingInterval: 10 * time.Second,
}

srv.AddTransport(upgrader)
srv.Use(extension.Introspection{})
if helpers.Env("MODE") == "PRODUCTION" {
    cache, err := apq.NewCache(24 * time.Hour)

    if err != nil {
        log.Fatalf("cannot create APQ redis cache: %v", err)
    }

    srv.Use(extension.AutomaticPersistedQuery{Cache: cache})
}


go initWorkers()

go runAsynqmon()
router.Use(getHeadersMiddleware())

router.Handle("/", srv)

if helpers.Env("MODE") == "DEVELOPMENT" {
    router.Handle("/playground", playground.Handler("GraphQL playground", "/"))
    log.Printf("connect to http://localhost:%s/playground for GraphQL playground", port)
}

log.Fatal(http.ListenAndServe(":"+port, router))

and here is my client code:

import { setContext } from '@apollo/client/link/context'
import { onError } from '@apollo/client/link/error'
import { GraphQLWsLink } from '@apollo/client/link/subscriptions'
import { getMainDefinition } from '@apollo/client/utilities'
import { createUploadLink } from 'apollo-upload-client'
import { createClient } from 'graphql-ws'

import { logout } from '../helpers/logout'
import { getTokenFromStorage } from '../helpers/userData'
import { lang } from '../localization'

const authLink = setContext((_, { headers }) => {
  const token = getTokenFromStorage()
  return {
    headers: {
      authorization: token ? `Bearer ${token}` : undefined,
      'Accept-Language': lang,
      ...headers
    }
  }
})
const httpLink = createUploadLink({
  uri: process.env.REACT_APP_GRAPH_BFF || 'http://localhost:8080'
})

const wsLink = new GraphQLWsLink(
  createClient({
    url: 'ws://localhost:8080/'
  })
)

const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query)
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    )
  },
  wsLink,
  httpLink
)

const logoutLink = onError(({ response }) => {
  if (
    response?.errors &&
    response.errors.length > 0 &&
    response.errors.some((errorItem) =>
      errorItem.message.toLowerCase().includes('unauthenticated')
    )
  ) {
    logout()
  }
})

const chainList = [logoutLink, authLink, splitLink]

const linkChain = from(chainList)

const apolloClient = new ApolloClient({
  cache: new InMemoryCache({
    addTypename: false
  }),
  link: linkChain
})

export default apolloClient

I thought CheckOrigin in upgrader would fix this but it didn't work any idea how to fix this?

1

There are 1 best solutions below

0
On BEST ANSWER

that was a awesome question. that means you are a talented programmer... and response is here:

you need to change this line:

srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &resolvers.Resolver{}}))

because NewDefaultServer use same origin that means your Origin and Host MUST be same and that means your code : CheckOrigin: func(r *http.Request) bool { return true }, did not work. you must change NewDefaultServer to New, so your code change to it:

srv := handler.New(generated.NewExecutableSchema(generated.Config{Resolvers: &resolvers.Resolver{}}))

and Your WebSocket works fine. be careful about this change because NewDefaultServer has some config inside it for Update or get or some other things. (see package itself) and you must write inside it handy in your code.