node.jsgraphqlrestifyexpress-graphql

Can't get CORS working with Restify + GraphQL setup


I've set up a GraphQL server using express-graphql and Restify, which works perfectly on Postman. However, when actually calling it from our frontend we keep getting CORS issues. I've tried just about everything.

The weird thing is that if we remove all headers from the frontend axios request, CORS is no longer an issue - but then we get the "query must be a string" error from graphql.

Full code:

const restify = require('restify');
const { graphqlHTTP } = require('express-graphql');
const corsMiddleware = require('restify-cors-middleware2');

const schema = require('./Schemas');
const { auth } = require('./middleware');
const { bugsnag } = require('./utils/config');

const PORT = process.env.PORT || 3004;
const app = restify.createServer();

app.use(bugsnag.requestHandler);

app.use(function crossOrigin(req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    const allowHeaders = ['Accept', 'Accept-Version', 'Content-Type', 'Api-Version', 'Origin', 'X-Requested-With']; // added Origin & X-Requested-With

    res.header('Access-Control-Allow-Headers', allowHeaders.join(', '));
    // res.header('Access-Control-Allow-Credentials', true); // tried both with and without this
    return next();
});

app.use(auth.authenticateUser);

app.post(
    '/graph',
    graphqlHTTP((req, res, graphQLParams) => {
        console.log(req);
        return {
            schema,
            context: {
                user: req.user,
            },
        };
    })
);

app.get(
    '/graph',
    graphqlHTTP({
        schema,
        graphiql: true,
    })
);

app.listen(PORT, () => console.log(`Listening on port ${PORT}!`));

Other things I've tried:

const cors = corsMiddleware({
    preflightMaxAge: 5,
    origins: ['*'],
    allowHeaders: ['X-App-Version'],
    exposeHeaders: [],
});
app.pre(cors.preflight);
app.use(cors.actual);

I also tried:

app.use(function crossOrigin(req, res, next) {
    console.log('Adding cors headers');
    const allowHeaders = ['Accept', 'Accept-Version', 'Content-Type', 'Api-Version', 'Origin', 'X-Requested-With']; 
    res.header('Access-Control-Allow-Credentials', 'true');
    res.header('Access-Control-Allow-Headers', allowHeaders.join(', '));
    res.header("Access-Control-Allow-Origin", "http://localhost:3000");
    next();
});

I also tried basically every combination of all of the above, and none worked. I even tried removing the auth header from the request and removing the auth middleware, and that didn't work either (although it did get us to the point where at least it was a 404 error due to "query not being a string" rather than the CORS issue.


Solution

  • This isn't very well documented in the restify-cors-middleware2 package, but turns out I need to set the allowCredentialsAllOrigins and credentials properties, as well as add a lot more allowHeaders:

    const cors = corsMiddleware({
        preflightMaxAge: 5,
        origins: ['*'],
        allowHeaders: [
            'X-App-Version',
            'Accept',
            'Accept-Version',
            'Content-Type',
            'Api-Version',
            'Origin',
            'X-Requested-With',
            'Authorization',
        ],
        exposeHeaders: [],
        credentials: true,
        allowCredentialsAllOrigins: true,
    });
    app.pre(cors.preflight);
    app.use(cors.actual);