CORS issues and 503 Service Unavailable errors while using Express, Node.js, and MySQL on Heroku

I am using NodeJS & Express, MySQL and React. I deployed the frontend and backend separately on Heroku.

I keep getting the error 503 Service Unavailable and CORS issue on the deployed application but when I run it locally it works fine, which is weird.

Here’s the console errors:

First error:

Access to XMLHttpRequest at ‘https://react-tws-hubspot-be-a7eecd7171c3.herokuapp.com/upload/contacts’ from origin ‘https://react-tws-hubspot-fe-b3d36e68376c.herokuapp.com’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

Second error:

POST https://react-tws-hubspot-be-a7eecd7171c3.herokuapp.com/upload/contacts net::ERR_FAILED 503 (Service Unavailable)

I’m using cors and configured it already:

app.use(cors({
    origin: 'https://react-tws-hubspot-fe-b3d36e68376c.herokuapp.com' || process.env.FRONTEND_URL,
    methods: ['GET', 'POST', 'OPTIONS', 'PUT'],
    credentials: true,
    allowedHeaders: ['Content-Type', 'Authorization'],
}));

app.options('*', cors());

Here’s my endpoint /upload/contacts that accepts a file upload and a multer middleware get file metadata (basically a function that extracts CSV data and upload it to HubSpot):

app.post('/upload/contacts', upload.array('files', 4), async (req, res) => {
    try {
        const contactBuffer = req.files[0].buffer;
        const companyBuffer = req.files[1].buffer;
        const contactBuffer2 = req.files[2].buffer;
        const projectBuffer = req.files[3].buffer;
        const filename = req.body.filename;
        const deal_stage = req.body.deal_stage;
        hubspot_api_key = req.body.hubspot_api_key;

        console.log('Parsing contact CSV');
        const Contact = await parseCsvBuffer(contactBuffer);

        console.log('Parsing company CSV');
        const Company = await parseCsvBuffer(companyBuffer);

        console.log('Importing to HubSpot');
        const importResponse = await importToHubspot(filename, contactBuffer2, companyBuffer, projectBuffer, hubspot_api_key);

        console.log('Waiting for operations to complete');
        const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
        await delay(12000);

        console.log('Fetching contacts cache');
        const contactsCache = await getAllContactsToCache(hubspot_api_key);

        console.log('Fetching deals cache');
        const dealsCache = await getAllDealsToCache(hubspot_api_key);


        if (importResponse !== 0) {
            const response = await createNewRecords(Contact, Company, contactsCache, dealsCache, broadcastProgress, hubspot_api_key, deal_stage);
            res.status(200).send(response);
        } else {
            res.status(400).send({ message: 'Import failed, no records were created.' });
        }

    } catch (error) {
        console.error(error.response ? error.response.data : error.message);
        res.status(error.response ? error.response.status : 500).send(error.response ? error.response.data : 'Server Error');
    }
})

Here’s my frontend request:

export async function sendToServer(fileName, contactBlob, companyBlob, contactBlob2, projectBlob, toggleModal, setLoading, hubspot_api_key, dealStage) {
    let form = new FormData();
    form.append('files', contactBlob, 'Construct Connect Contacts Main.csv');
    form.append('files', companyBlob, 'Construct Connect Company.csv');
    form.append('files', contactBlob2, 'Construct Connect Contacts.csv');
    form.append('files', projectBlob, 'Construct Connect Projects.csv');
    form.append('filename', fileName);
    form.append('hubspot_api_key', hubspot_api_key);
    form.append('deal_stage', dealStage);

    if (hubspot_api_key !== '') {
        try {
            setLoading(true);
            const res = await axios.post(`${BASE_URL}/upload/contacts`, form, {
                headers: {
                    'Content-Type': 'multipart/form-data',
                }
            });

            if (res.status === 200) {
                console.log(`Data from server: ${res.data}`);
                console.log(`Message from server: ${res.message}`);
                toggleModal("Success");
            } else {
                toggleModal("Failed");
            }

        } catch (error) {
            console.log(`Error sending contact and company data to backend server. Error: ${error}`);
        } finally {
            setLoading(false);
        }
    } else {
        console.log("There's no hubspot api key!");
    }
}

Just like I said, I can complete file upload when I run my code locally but it doesnt work on the deployed application in heroku.

I tried adding cors middleware to whitelist the origins, I’ve also checked my env variables (both dev and production env - on heroku), tried running it locally (works great btw).

The issues you’re experiencing—specifically the 503 Service Unavailable and CORS errors—when deploying to Heroku are quite common in cases where backend and frontend are hosted separately. Let’s break down the issues and address them step by step.

1. 503 Service Unavailable

This error indicates that the backend service is not reachable or available. Here are a few reasons why this might happen on Heroku:

Possible Causes:

  • Backend is sleeping: If you’re using a free Heroku plan, your backend might be going to sleep when idle, causing downtime.
  • Heroku Dyno Crashes: If your backend process crashes on Heroku, the dyno will restart and you may intermittently see a 503 error.
  • Incorrect environment configuration: There could be misconfigurations related to environment variables, database connections, or ports in production that cause the backend to fail.

Fixes:

  • Check Heroku logs: Run the following command to get detailed logs and see what’s causing the 503 error:
heroku logs --tail --app <your-backend-app-name>

Look for errors in the logs, such as unhandled exceptions, environment variable issues, or database connection problems.

  • Ensure Backend Dynos are Running: Check your dyno status with:
heroku ps --app <your-backend-app-name>

If the dynos are sleeping or crashed, try scaling them up:

heroku ps:scale web=1 --app <your-backend-app-name>
  • Check Environment Variables: Make sure all environment variables required by your backend (like DATABASE_URL, PORT, etc.) are correctly set in the Heroku dashboard under the Config Vars section.
  • Use Heroku Scheduler for Free Dynos: If you are using a free Heroku plan and your backend is sleeping due to inactivity, consider setting up Heroku Scheduler to periodically ping your backend to keep it awake.

2. CORS Issue

The CORS issue is happening because your backend is not allowing requests from your frontend’s origin (https://react-tws-hubspot-fe-b3d36e68376c.herokuapp.com). This needs to be explicitly allowed in the backend configuration.

Fixes:

  • Correct CORS Configuration: Update your CORS middleware configuration in your backend to ensure the frontend origin is whitelisted correctly.Here’s a corrected version of your CORS setup:
const cors = require('cors');

app.use(cors({
    origin: 'https://react-tws-hubspot-fe-b3d36e68376c.herokuapp.com', // Allow the frontend URL
    methods: ['GET', 'POST', 'OPTIONS', 'PUT'], // Allow specific methods
    credentials: true, // Allow cookies/auth headers
    allowedHeaders: ['Content-Type', 'Authorization'], // Allow these headers
}));

app.options('*', cors()); // Allow preflight requests
  • Debugging CORS in DevTools: Open your browser’s Developer Tools and check the Network tab for the OPTIONS request. This is a preflight request that the browser sends before the actual POST request to verify if CORS is allowed. If the OPTIONS request is failing, it may be related to how Heroku is handling your backend server’s CORS policy.
  • Allow All Origins Temporarily for Testing: To verify that it’s indeed a CORS issue, you can temporarily allow all origins and see if it resolves the problem:
app.use(cors({
    origin: '*',
    methods: ['GET', 'POST', 'OPTIONS', 'PUT'],
    credentials: true,
    allowedHeaders: ['Content-Type', 'Authorization'],
}));

If this resolves the issue, you can refine the CORS policy to be more specific afterward.

3. Other Considerations

  • Backend Port Configuration: Make sure your backend is listening on the correct port in production. Heroku dynamically assigns a port, which must be accessed via the PORT environment variable. In your backend, ensure you’re using this:
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});
  • Proxy Setting in React App: If you’re using the create-react-app framework for your frontend, ensure you’ve configured the proxy correctly in package.json or via CORS in production. If you’re using a proxy in development (like "proxy": "http://localhost:5000"), make sure it is properly handled for the production deployment by making API requests directly to the backend URL in your code.Example:
const BASE_URL = process.env.REACT_APP_API_URL || 'https://react-tws-hubspot-be-a7eecd7171c3.herokuapp.com';

Ensure that REACT_APP_API_URL is set correctly in the Heroku environment variables for the frontend.

4. Diagnosing Deployment vs. Local Issues

Since it works locally but not on Heroku, this suggests something specific to the production environment (Heroku) is misconfigured. Here’s a checklist for diagnosing such discrepancies:

  • Check environment variable values: Ensure production environment variables are set correctly on Heroku for both frontend and backend.
  • Network differences: Locally, your requests might be going to localhost, whereas in production, they’re across the internet. Check if there are network-related issues, such as security groups, firewalls, or domains being blocked.
  • Check Heroku Resources: Verify if you’re exceeding any resource limits on Heroku, like request timeouts, memory, or connection limits.

Conclusion

  1. Diagnose and fix the 503 error by checking logs and dyno health.
  2. Resolve the CORS issue by updating your backend CORS configuration to correctly allow requests from your frontend origin.
  3. Ensure all environment variables are correctly configured in the Heroku production environment.
  4. Temporarily relax CORS rules and test for network or configuration issues between frontend and backend.

Try these steps, and let me know if the issue persists!