2
votes

I am working on an app using a React frontend and Express backend, with GraphQL setup through Apollo (I am following and modifying tutorial https://www.youtube.com/playlist?list=PLN3n1USn4xlkdRlq3VZ1sT6SGW0-yajjL)

I am currently attempting deployment, and am doing so with Heroku. Everything functions perfectly on my local machine before deployment and on Heroku in Google Chrome. However, I get the aforementioned errors in Safari and Firefox, respectively. Wondering why this is happening in these browsers and how to fix.

I have spent about 10 hrs doing research on this. Things I tried that made no difference:

  • I tried adding CORS to my express backend
  • I tried serving the graphql endpoint as HTTPS
  • Moving app.use(express.static) in main app.js server file

I couldn't find many other things to try. Everywhere I looked seemed to say that CORS fixed the problem, but mine persists.

Github link: https://github.com/LucaProvencal/thedrumroom Live Heroku App: https://powerful-shore-83650.herokuapp.com/

App.js (express backend):

const cors = require('cors')
// const fs = require('fs')
// const https = require('https')
// const http = require('http')

app.use(express.static(path.join(__dirname, 'client/build')));
app.use(cors('*')); //NEXT TRY app.use(cors('/login')) etc...
app.use(cors('/*'));
app.use(cors('/'));
app.use(cors('/register'));
app.use(cors('/login'));
app.get('/login', (req, res) => {
    res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});


app.get('/register', (req, res) => {
    res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});


server.applyMiddleware({ app }); // app is from the existing express app. allows apollo server to run on same listen command as app

const portVar = (process.env.PORT || 3001) // portVar cuz idk if it will screw with down low here im tired of dis

models.sequelize.sync(/*{ force: true }*/).then(() => { // syncs sequelize models to postgres, then since async call starts the server after
    app.listen({ port: portVar }, () =>
      console.log(`???? ApolloServer ready at http://localhost:3001${server.graphqlPath}`)
    )
    app.on('error', onError);
    app.on('listening', onListening);
});


app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

Full file is on Github, I tried to post only relevant parts above.

The expected result is that it works in all browsers. It seems from my research that since Heroku serves on HTTPS, Safari and Firefox do not allow requests to HTTP (which is where the graphql server is located, http://localhost:3001/graphql'). When I tried serving Apollo on HTTPS, Heroku just crashed, giving me H13 and 503 errors.

Thanks for any help...

2

2 Answers

2
votes

Was going to delete this because it is such a silly problem but maybe it will help someone in the future:

I simply replaced all of my 'http://localhost:PORT' endpoints in development with '/graphql'. I assumed that localhost meant local the machine running the code. But an app running on Heroku does not point to localhost. The express server is served on the url (https://powerful-shore-83650.herokuapp.com/) in our case...

At any rate I am so glad I came to a solution. I have a full stack app deployed and connected to a db. Hopefully this post can save someone lots of time.

0
votes

This may also happen during local development when running the front end using HTTPS, but the back end using HTTP.

This is because CORS treats two URLs as having the same origin "only when the scheme, host, and port all match". Matching scheme means matching protocols e.g. both http, or both https.

One solution for local development is to proxy the back end using a tool such as ngrok.


Suppose the front end uses an environment variable which indicates the back end's URL:

BACK_END_API_URL=http://localhost:3005. Then do the following.

  • Install ngrok
  • Identify what port the back end is running on e.g. 3005
  • Run ngrok http 3005 at the command line, which will establish both http and https endpoints. Both will ultimately proxy the requests to the same back end endpoint: http://localhost:3005
  • After running ngrok it will display the http and https endpoints you can use. Put the one that matches the front end protocol you're using (e.g. https) into your front end environment variable that indicates the back end's URL e.g.

BACK_END_API_URL=https://1234asdf5678ghjk.ngrok.io