1

I'm using gqlgen for my GraphQL API and I have some subscription (websocket) in my API. But it's not working as expected:

When initFunc of the transport.Websocket{} return an error the websocket is not close but spam the init function.

I would have expected the websocket to be closed and only one request is made to the server.

How can I can properly close the websocket if the init function failed (for instance auth failed) ?


Here is an example of an init funct that have this issue. It is from the gqlgen websocket example. I just added a debug print to make things clear in the webSocketInit:

func webSocketInit(ctx context.Context, initPayload transport.InitPayload) (context.Context, error) {
    // Get the token from payload
    any := initPayload["authToken"]
    token, ok := any.(string)
    if !ok || token == "" {
        fmt.Println("error in init")
        return nil, errors.New("authToken not found in transport payload")
    }

    // Perform token verification and authentication...
    userId := "john.doe" // e.g. userId, err := GetUserFromAuthentication(token)

    // put it in context
    ctxNew := context.WithValue(ctx, "username", userId)

    return ctxNew, nil
}

and then just run a query in the playground without any auth header:

subscription test {
  subscribe(subscriber:"jj")
}

the browser will be hanging in a loading cycle and the server is looping in the init function:

➜  websocket_gqlgen ./server                                  
2022/12/20 09:40:56 connect to http://localhost:8080/ for GraphQL playground
error in init
error in init
error in init
error in init
error in init
...

The complete server and gql schema can be found in the gqlgen example repo but here is the main function copy paste for your convenience:

const defaultPort = "8080"

func main() {
    port := os.Getenv("PORT")
    if port == "" {
        port = defaultPort
    }

    router := chi.NewRouter()

    // CORS setup, allow any for now
    // https://gqlgen.com/recipes/cors/
    c := cors.New(cors.Options{
        AllowedOrigins:   []string{"*"},
        AllowCredentials: true,
        Debug:            false,
    })

    srv := handler.New(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}}))
    srv.AddTransport(transport.POST{})
    srv.AddTransport(transport.Websocket{
        KeepAlivePingInterval: 10 * time.Second,
        Upgrader: websocket.Upgrader{
            CheckOrigin: func(r *http.Request) bool {
                return true
            },
        },
        InitFunc: func(ctx context.Context, initPayload transport.InitPayload) (context.Context, error) {
            return webSocketInit(ctx, initPayload)
        },
    })
    srv.Use(extension.Introspection{})

    router.Handle("/", playground.Handler("My GraphQL App", "/app"))
    router.Handle("/app", c.Handler(srv))

    log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
    log.Fatal(http.ListenAndServe(":"+port, router))

}
cylon86
  • 550
  • 4
  • 20

0 Answers0