Search code examples
reactjsmongodbexpressherokumulter

ExpressJS API Deployed on Heroku Gives 500 Internal Server Error


I built my first MERN app and everything works in my local development environment, but ran into some issues when I deployed my ExpressJS REST API on Heroku. After deployment, most GET/POST/PATCH requests sent to the backend are working. However, I have one POST and one PATCH request which include images in the request that are returning a 500 Internal Server Error. For these requests, I am using Axios and the images are appended to the request as FormData.

The following are the logs I get with the command heroku logs -t:

2021-02-03T22:00:04.722403+00:00 app[web.1]: RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: ENOENT
2021-02-03T22:00:04.722443+00:00 app[web.1]: at ServerResponse.writeHead (_http_server.js:248:11)
2021-02-03T22:00:04.722444+00:00 app[web.1]: at ServerResponse._implicitHeader (_http_server.js:239:8)
2021-02-03T22:00:04.722445+00:00 app[web.1]: at write_ (_http_outgoing.js:654:9)
2021-02-03T22:00:04.722445+00:00 app[web.1]: at ServerResponse.end (_http_outgoing.js:766:5)
2021-02-03T22:00:04.722446+00:00 app[web.1]: at ServerResponse.send (/app/node_modules/express/lib/response.js:221:10)
2021-02-03T22:00:04.722446+00:00 app[web.1]: at ServerResponse.json (/app/node_modules/express/lib/response.js:267:15)
2021-02-03T22:00:04.722448+00:00 app[web.1]: at /app/app.js:51:4
2021-02-03T22:00:04.722448+00:00 app[web.1]: at Layer.handle_error (/app/node_modules/express/lib/router/layer.js:71:5)
2021-02-03T22:00:04.722449+00:00 app[web.1]: at trim_prefix (/app/node_modules/express/lib/router/index.js:315:13)
2021-02-03T22:00:04.722449+00:00 app[web.1]: at /app/node_modules/express/lib/router/index.js:284:7
2021-02-03T22:00:04.722450+00:00 app[web.1]: at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)
2021-02-03T22:00:04.722450+00:00 app[web.1]: at next (/app/node_modules/express/lib/router/index.js:275:10)
2021-02-03T22:00:04.722451+00:00 app[web.1]: at Layer.handle_error (/app/node_modules/express/lib/router/layer.js:67:12)
2021-02-03T22:00:04.722451+00:00 app[web.1]: at trim_prefix (/app/node_modules/express/lib/router/index.js:315:13)
2021-02-03T22:00:04.722451+00:00 app[web.1]: at /app/node_modules/express/lib/router/index.js:284:7
2021-02-03T22:00:04.722452+00:00 app[web.1]: at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12)
2021-02-03T22:00:04.724981+00:00 heroku[router]: at=info method=PATCH path="/api/user/profilePic/601721519ab4390015d2439a" host=mycookbook-backend.herokuapp.com request_id=6d1bb670-2c07-4078-833a-4ae3ae383be2 fwd="99.229.62.182" dyno=web.1 connect=1ms service=20ms status=500 bytes=627 protocol=https

I also tried printing the err.response object in the console when the frontend receives the response from the backend:

err.response: {
    "data":
        "<!DOCTYPE html>
            <html lang=\"en\">
                <head>
                    <meta charset=\"utf-8\">
                    <title>Error</title>
                </head>
                <body>
                    <pre>Internal Server Error</pre>
                </body>
            </html>",
    "status":500,
    "statusText":"Internal Server Error",
    "headers":{
        "content-length":"148",
        "content-type":"text/html; charset=utf-8"
        },
    "config":{
        "url":"/user/profilePic/userId",
        "method":"patch",
        "data":{},
        "headers":{
            "Accept":"application/json, text/plain, */*",
            "Authorization":"Bearer authToken"
            },
        "baseURL":"https://mycookbook-backend.herokuapp.com/api/",
        "transformRequest":[null],
        "transformResponse":[null],
        "timeout":0,
        "xsrfCookieName":"XSRF-TOKEN",
        "xsrfHeaderName":"X-XSRF-TOKEN",
        "maxContentLength":-1,
        "maxBodyLength":-1
        },
    "request":{}
    }

In my MongoDB database, I made sure to give network access to 0.0.0.0/0 so that the backend on Heroku can access it. I also doubled checked my config vars on Heroku, and these should be correct since the other requests are working fine. Any tips or guidance for solving this issue is much appreciated.

EDIT: I forgot to mention I am using multer to do the image upload. I am also aware the Heroku filesystem does not allow permanent storage and that Amazon S3 would be a good alternative to use. I am just wondering if temporary image upload is still possible as a first step, or should I just go straight to implementing storage on Amazon S3?

EDIT 2: One more thing I tried today - I read on Heroku Node.js deploy troubleshooting page that its possible there can be a mismatch in node and npm versions between local and production environment. So I checked my latest build log on Heroku and noticed my node and npm versions were higher than my local environment. I tried editing my package.json file by adding an "engines" section where I can specify my exact versions of node and npm that I had in the local environment. However, still no luck as requests that upload images still fail.


Solution

  • I was able to figure out my mistake and solve my own question.

    I got an idea from the top answer of this question: uploading image to heroku using node and multer not work

    The answer says that if your local 'images' folder that multer is uploading to is empty, Heroku will not create the folder on the server when you deploy it. In my case, I had some images from testing already inside that folder when I deployed - the issue was that I had my 'images' folder path in my .gitignore file. I did this because this project is on my personal GitHub and I didn't want to check in these images. I used Git to deploy on Heroku, and by adding the images folder in the .gitignore, my image folder was not being included in my deployment build. Removing it from the .gitignore allowed my image upload requests to work.

    Git does not track empty folders so ensure you've manually added a little dummy data into any empty folder that is used by the application. For example those used by multer. This prevents POST, PATCH and PUT request errors on heroku.