How to deploy React + Express application to Heroku?

Viewed 69

I have a React & Express application but I can't deploy this app to Heroku.

My file structure is like that:

  • client
    • public
    • src
    • package.json
  • index.js
  • package.json

client/package.json:

{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "proxy": "http://localhost:5000",
  "dependencies": {
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "4.0.3",
    "styled-components": "^5.3.3",
    "web-vitals": "^1.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "babel-plugin-macros": "^3.1.0",
    "babel-plugin-styled-components": "^2.0.2"
  }
}

package.json

{
  "name": "moviefy",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "engines": {
    "node": "14.17.3",
    "npm": "6.14.11"
  },
  "cacheDirectories": [
    "node_modules",
    "client/node_modules"
  ],
  "scripts": {
    "start": "concurrently --kill-others-on-fail \"npm run server\" \"npm run client\"",
    "server": "nodemon index.js",
    "client": "cd client && npm run start",
    "postinstall": "cd client && npm install",
    "build": "NODE_ENV=production cd client && npm install && npm run build",
    "start-server": "node index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.24.0",
    "dotenv": "^10.0.0",
    "express": "^4.17.1",
    "react-component-export-image": "^1.0.6",
    "react-router-dom": "^5.2.0"
  },
  "devDependencies": {
    "concurrently": "^6.4.0",
    "nodemon": "^2.0.15"
  }
}

index.js:

require("dotenv").config();
const express = require("express");
const axios = require("axios");
const app = express();
const path = require("path");
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const REDIRECT_URI = process.env.REDIRECT_URI;
const FRONTEND_URI = process.env.FRONTEND_URI;
const PORT = process.env.PORT || 5000;

app.use(express.static(path.resolve(__dirname, "./client/build")));

app.get("/", (req, res) => {
  res.send("Hello");
});

/**
 * Generates a random string containing numbers and letters
 * @param  {number} length The length of the string
 * @return {string} The generated string
 */
const generateRandomString = (length) => {
  let text = "";
  const possible =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  for (let i = 0; i < length; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }
  return text;
};

const stateKey = "spotify_auth_state";

app.get("/login", (req, res) => {
  const state = generateRandomString(16);
  res.cookie(stateKey, state);

  const scope = [
    "user-read-private",
    "user-read-email",
    "user-top-read",
    "playlist-read-private",
    "playlist-read-collaborative",
    "user-top-read",
    "user-follow-read",
  ].join(" ");

  const query = {
    client_id: CLIENT_ID,
    response_type: "code",
    redirect_uri: REDIRECT_URI,
    state: state,
    scope: scope,
  };
  let queryParam = new URLSearchParams(query);
  res.redirect(`https://accounts.spotify.com/authorize?${queryParam}`);
});

app.get("/callback", (req, res) => {
  const code = req.query.code || null;
  const data = {
    grant_type: "authorization_code",
    code: code,
    redirect_uri: REDIRECT_URI,
  };
  axios({
    method: "post",
    url: "https://accounts.spotify.com/api/token",
    data: new URLSearchParams(data),
    headers: {
      "content-type": "application/x-www-form-urlencoded",
      Authorization: `Basic ${new Buffer.from(
        `${CLIENT_ID}:${CLIENT_SECRET}`
      ).toString("base64")}`,
    },
  })
    .then((response) => {
      if (response.status === 200) {
        const { refresh_token, access_token, expires_in } = response.data;
        const queryParams = new URLSearchParams({
          access_token,
          refresh_token,
          expires_in,
        });

        res.redirect(`${FRONTEND_URI}/?${queryParams}`);
      } else {
        res.redirect(`/?${JSON.stringify({ error: "invalid_token" })}`);
      }
    })
    .catch((error) => {
      res.send(error);
    });
});

app.get("/refresh_token", (req, res) => {
  const { refresh_token } = req.query;

  axios({
    method: "post",
    url: "https://accounts.spotify.com/api/token",
    data: new URLSearchParams({
      grant_type: "refresh_token",
      refresh_token: refresh_token,
    }),
    headers: {
      "content-type": "application/x-www-form-urlencoded",
      Authorization: `Basic ${new Buffer.from(
        `${CLIENT_ID}:${CLIENT_SECRET}`
      ).toString("base64")}`,
    },
  })
    .then((response) => {
      res.send(response.data);
    })
    .catch((error) => {
      res.send(error);
    });
});

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

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

And I got this error when I run "heroku logs" command in my console:

2021-12-13T20:43:20.583181+00:00 heroku[web.1]: Starting process with command `npm start`
2021-12-13T20:43:22.310970+00:00 app[web.1]:
2021-12-13T20:43:22.311002+00:00 app[web.1]: > moviefy@1.0.0 start /app
2021-12-13T20:43:22.311005+00:00 app[web.1]: > concurrently --kill-others-on-fail "npm run server" "npm run client"
2021-12-13T20:43:22.311006+00:00 app[web.1]:
2021-12-13T20:43:22.314863+00:00 app[web.1]: sh: 1: concurrently: not found
2021-12-13T20:43:22.319859+00:00 app[web.1]: npm ERR! code ELIFECYCLE
2021-12-13T20:43:22.320034+00:00 app[web.1]: npm ERR! syscall spawn
2021-12-13T20:43:22.320108+00:00 app[web.1]: npm ERR! file sh
2021-12-13T20:43:22.320194+00:00 app[web.1]: npm ERR! errno ENOENT
2021-12-13T20:43:22.322717+00:00 app[web.1]: npm ERR! moviefy@1.0.0 start: `concurrently --kill-others-on-fail "npm run server" "npm run client"`

I tried to use "client/build" structure in index.js file but it didn't work too. Also, I tried "heroku-postbuild" command in package.json file but I still got same error in heroku.

0 Answers
Related