4

I'm trying to setup up a basic stack using React, Apollo served by graphQL served by Go through gqlgen.

I'm following the getting started guide for gqlgen, which works fine.

I'm also following the getting started guide for Apollo, which works fine.

My trouble lies in the chrome not allowing me to ping the backend when I try to stitch the two together. I get a CORS preflight header error which is understandable, considering React is on localhost:3000 and the Go server is on localhost:8080

gqlgen provided a helpful example on how to get CORS running on the Go server here using chi and go-cors. However the example provided is based of the starwars example of gqlgen rather then the getting-started example.

I modified the CORS example to the following:

package main

import (
    "net/http"

    "github.com/99designs/gqlgen/graphql/handler"
    "github.com/99designs/gqlgen/graphql/handler/transport"
    "github.com/99designs/gqlgen/graphql/playground"
    "github.com/FlockSupport/graphql/graph"
    "github.com/FlockSupport/graphql/graph/generated"
    "github.com/go-chi/chi"
    "github.com/gorilla/websocket"
    "github.com/rs/cors"
)

func main() {
    router := chi.NewRouter()

    // Add CORS middleware around every request
    // See https://github.com/rs/cors for full option listing
    router.Use(cors.New(cors.Options{
        AllowedOrigins:   []string{"http://localhost:8080"},
        AllowCredentials: true,
        Debug:            true,
    }).Handler)

    // srv := handler.New(starwars.NewExecutableSchema(starwars.NewResolver())) // MODIFIED THIS.
    srv := handler.New(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))

    srv.AddTransport(&transport.Websocket{
        Upgrader: websocket.Upgrader{
            CheckOrigin: func(r *http.Request) bool {
                // Check against your desired domains here
                return r.Host == "example.org"
            },
            ReadBufferSize:  1024,
            WriteBufferSize: 1024,
        },
    })

    router.Handle("/", playground.Handler("GraphQL Playground", "/query"))
    // router.Handle("/", handler.Playground("Starwars", "/query")) // MODIFIED THIS.
    router.Handle("/query", srv)

    err := http.ListenAndServe(":8080", router)
    if err != nil {
        panic(err)
    }
}

The server runs, but testing queries out through the playground (localhost:8080 so CORS shouldn't matter here) gives the following error:

{
  "error": {
    "errors": [
      {
        "message": "transport not supported"
      }
    ],
    "data": null
  }
}

This also doesn't work even if I setup the Chi router only:


func main() {
    router := chi.NewRouter()

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

    router.Handle("/", playground.Handler("GraphQL Playground", "/query"))
    router.Handle("/query", srv)

    err := http.ListenAndServe(":8080", router)
    if err != nil {
        panic(err)
    }
}

I get a Server Cannot Be Reached when I access localhost:8080 playground.

How can I enable CORS properly on the gqlgen Go server so React can ping it?

Carrein
  • 3,231
  • 7
  • 36
  • 77
  • You seem to be setting the Go server's address as an allowed origin. That doesn't make sense. If the server were the origin you wouldn't need the header. You have to allow the server that serves the JavaScript, i.e. whatever appears in your browser in the address bar. – Peter Mar 28 '20 at 11:51
  • Right, but I'm accessing localhost:8080 to test through the playground and that is the response I get. – Carrein Mar 28 '20 at 11:57

3 Answers3

3

You need to provide CORS checks as middleware. For instance you could use "github.com/rs/cors". It could looks like following code

mux.Handle("/playground", handler.Playground("API", "/gql"))
mux.Handle("/gql",
    handler.GraphQL(
        gen.NewExecutableSchema(gen.Config{Resolvers: resolver.New(dep)}),
        handler.ErrorPresenter(errorCustomize)))
h := cors.AllowAll().Handler(util.JwtMiddleware(mux))

srv := &http.Server{
    Addr:    ":8087",
    Handler: h,
}
Maxim
  • 2,233
  • 6
  • 16
3

I had the same issue, and solved it by using the NewDefaultServer method instead of New.

srv := handler.NewDefaultServer(.....)
dueruen
  • 46
  • 2
  • 1
    I have the same issue too and I did use srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}})) but that didn't solve my problem.... :( – Annie Aug 17 '20 at 18:31
  • Indeed `srv := handler.NewDefaultServer(.....)` was helpful, because this function adds support for `POST` http method. But also it is important to make sure the http header: `'content-type: application/json'` – C. Damoc Jun 15 '21 at 14:45
2

I had the same problem but actually it was because, when I did the request to the server I didn't add the header Content-Type: application/json. curiously I had to include the Content-Length header when using postman.

so, when I tried:

    curl --request POST \
    --url http://localhost:8000/ \
    --data '{"query":"query {\n  televisions {\n    id\n  }\n}"}'

This was the response:

{"errors":[{"message":"transport not supported"}],"data":null}

But when I added the header:

    curl --request POST \
    --url http://localhost:8000/ \
    --header 'content-type: application/json' \
    --data '{"query":"query {\n  televisions {\n    id\n  }\n}"}'

The server response normally:

{"data":{"televisions":[]}}