Hi all. I have pretty simple graphql-server:
import express from 'express'
import bodyParser from 'body-parser'
import { graphqlExpress, graphiqlExpress } from 'graphql-server-express'
import { makeExecutableSchema, addResolveFunctionsToSchema } from 'graphql-tools'
import Schema from './schema'
import Resolvers from './resolvers'
const executableSchema = makeExecutableSchema({
typeDefs: Schema,
printErrors: true,
});
addResolveFunctionsToSchema(executableSchema, Resolvers)
const PORT = 3000;
const app = express();
app.use('/graphql', bodyParser.json(), graphqlExpress(
{
schema: executableSchema,
resolvers: Resolvers,
},
))
app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }))
app.listen(PORT, () => console.log('Server is running!'))
With graphiql
tool everything working correctly: I can make requests and receive expected results. But when I am trying to send request with apollo-client
, something wrong. Here is my client code:
import ApolloClient, { createNetworkInterface } from 'apollo-client';
import gql from 'graphql-tag';
const apolloClient = new ApolloClient({
networkInterface: createNetworkInterface({
uri: 'http://localhost:3000/graphql',
opts: {
mode: 'no-cors',
},
}),
});
const query = gql`
{
polls {
title
}
}
`
apolloClient.query({
query,
}).then(result => {
console.log(result)
})
Here is errors:
Here is request details and response:
I am using next versions:
"apollo-client": "^0.8.0",
"graphql": "^0.9.0",
"graphql-server-express": "^0.5.2",
"graphql-tag": "^1.2.3",
"graphql-tools": "^0.9.2",
This looks like a graphql-server
issue. If you havenât already could you open against that repo?
You'll see that error when you're not passing the query/mutation to graphql-server. This is definitely an error on the server and not on the client.
@helfer Ok, so I have created simple repo to reproduce this error. I think you can reopen this issue again :)
https://github.com/semanser/graphql-test
Please follow steps in the readme file to run the server.
Sorry to keep bouncing you back and forth @semanser, but I took a look at your test case and I think this is a graphql-server
issue after all đ
As you can see in your ârequest details and responseâ image, the Content-Type
header is text/plain
. This will lead to an error on the server as it expects application/json
. However, if we look at the code, Apollo Client explicitly sets the Content-Type
header to application/json
, and if you run a minimum reproduction of the fetch in the console you get the same error. Here is what I ran in the developer console:
fetch('http://localhost:3000/graphql', {
method: 'POST',
mode: 'no-cors',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: '{ users { name } }' }),
})
It got the same error. So I opened up GraphiQL and ran the same code. The same error occurred. When you remove mode: 'no-cors'
in the GraphiQL error console, then the error goes away! However, you canât remove mode: 'no-cors'
in the app.
My hypothesis to whatâs happening is that the browser makes a HEAD
request to the GraphQL server to get the CORS options, but the server just returns a 405
saying only GET
and POST
requests are allowed. So the browser isnât told to allow the Content-Type
header though the CORS Access-Control-Allow-Headers
header.
So we have two options. We could either add CORS headers like Access-Control-Allow-Headers
to graphql-server
, or you could add the CORS headers yourself. I like the latter option best because we donât want to introduce security vulnerabilities in peopleâs servers by including CORS headers when the user is unsuspecting. To add CORS headers yourself, just do the following:
app.head('/graphql', (req, res) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:9000');
res.header('Access-Control-Request-Method', 'GET, POST');
res.header('Access-Control-Allow-Headers', 'Origin, Accept, Content-Type, Content-Length');
res.end();
});
âŠor to avoid the roundtrip required by CORS you could proxy your API using Webpack Dev Server by changing your configuration in webpack.config.js
to:
module.exports = {
...
devServer: {
contentBase: './app',
compress: true,
port: 9000,
historyApiFallback: {
index: 'index.html',
},
proxy: {
'/graphql': {
target: 'http://localhost:3000/graphql',
secure: false,
},
},
},
};
âŠand then change your client declaration to:
const apolloClient = new ApolloClient({
networkInterface: createNetworkInterface({
uri: 'http://localhost:9000/graphql',
}),
});
Note how the port is now 9000
and there is no mode: 'no-cors'
.
I personally like the proxy method best.
Some general purpose CORS resources:
We hit this issue early in PostGraphQL. Our solution there was to provide an option to enable CORS headers for users who wanted to opt in. If youâd like more information about our solution there, here are some resources:
@calebmer Thanks a lot for explanation! I have tried both variants but it's still doesn't work and that's very strange... I will continue to work to find solution for this problem.
Hey there,
Are there any solutions yet?
I've got the same problem. :-(
@kurmeroli see: https://github.com/apollographql/apollo-client/issues/1233#issuecomment-276108337
@kurmeroli The first solution doesn't work for me. I tried with:
$ npm install cors --save
with
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
And works fine. The same for ES6:
import cors from 'cors';
let app = express();
app.use(cors({}));
Issue solved me by adding CORS headers
const corsOptions = {
origin(origin, callback){
callback(null, true);
},
credentials: true
};
graphQLServer.use(cors(corsOptions));
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
}
graphQLServer.use(allowCrossDomain);
Most helpful comment
Sorry to keep bouncing you back and forth @semanser, but I took a look at your test case and I think this is a
graphql-server
issue after all đAs you can see in your ârequest details and responseâ image, the
Content-Type
header istext/plain
. This will lead to an error on the server as it expectsapplication/json
. However, if we look at the code, Apollo Client explicitly sets theContent-Type
header toapplication/json
, and if you run a minimum reproduction of the fetch in the console you get the same error. Here is what I ran in the developer console:It got the same error. So I opened up GraphiQL and ran the same code. The same error occurred. When you remove
mode: 'no-cors'
in the GraphiQL error console, then the error goes away! However, you canât removemode: 'no-cors'
in the app.My hypothesis to whatâs happening is that the browser makes a
HEAD
request to the GraphQL server to get the CORS options, but the server just returns a405
saying onlyGET
andPOST
requests are allowed. So the browser isnât told to allow theContent-Type
header though the CORSAccess-Control-Allow-Headers
header.So we have two options. We could either add CORS headers like
Access-Control-Allow-Headers
tographql-server
, or you could add the CORS headers yourself. I like the latter option best because we donât want to introduce security vulnerabilities in peopleâs servers by including CORS headers when the user is unsuspecting. To add CORS headers yourself, just do the following:âŠor to avoid the roundtrip required by CORS you could proxy your API using Webpack Dev Server by changing your configuration in
webpack.config.js
to:âŠand then change your client declaration to:
Note how the port is now
9000
and there is nomode: 'no-cors'
.I personally like the proxy method best.
Some general purpose CORS resources:
We hit this issue early in PostGraphQL. Our solution there was to provide an option to enable CORS headers for users who wanted to opt in. If youâd like more information about our solution there, here are some resources: