Reputation: 1277
Summary of the issue:
When deploying to Heroku a flask/react application, I'm having trouble running two buildpacks at once and making the application function. I typically am encountering 1 of 2 issues depending on how I setup the project.
cd static && npm --dev install && npm run build:production
in package.json postinstall script, my bundle.js file can't be found and all my components are reportedly not accessible: Here is an example error I get from Heroku in the terminal after a successful build:
ERROR in ./src/containers/HomeContainer/index.js
remote: Module not found: Error: Cannot resolve 'file' or 'directory' ../../components/IntroSection in /tmp/build_d95bc1f5e53719f4bd91a1a3/static/src/containers/HomeContainer
remote: resolve file
cd static && npm --dev install && npm run build:production && npm start
I see the the application builds properly and attempt to listen on the server port. However, eventually the terminal times out and never finished it seems? Here's how it looks in the terminal towards the last command:
remote: > node bin/server.js
remote:
remote: Listening on: {"address":"::","family":"IPv6","port":8080}
remote: webpack built e95b297d680022fe in 23191ms
The terminal remains stopped here.
In both instances, I can access the python side of the application no problem. The issue is getting the client side (react/redux) to render. When running this all locally in either version, it functions perfectly fine. No component errors and the server starts right up. By the way, I was told that Heroku needs package.json in root to help it run the setup. As a result, I've now got 2 package.json files. One in root and one in static. The one in root is just to help it download node/npm and push it over to the other package.json file (I think?).
Here's some detail I can give to try to debug the problem further:
-- Procfile:
web: gunicorn main:app
-- Buildpacks on Heroku:
heroku buildpacks:set heroku/python
heroku buildpacks:add heroku/nodejs
--Package.json in root:
{
"name": "something",
"version": "0.0.1",
"engines": { "node": "6.11.1", "npm": "3.10.10" },
"scripts": {
"postinstall": "cd static && npm --dev install && npm run build:production && npm start"
}
}
-- Package.json in static:
{
"name": "redux-easy-boilerplate",
"version": "1.3.3",
"description": "",
"scripts": {
"clean": "rimraf dist",
"build": "webpack --progress --verbose --colors --display-error-details --config webpack/common.config.js",
"build:production": "npm run clean && npm run build",
"lint": "eslint src",
"start": "node bin/server.js",
"test": "karma start"
},
"repository": {
"type": "git",
"url": ""
},
"keywords": [
"react",
"reactjs",
"boilerplate",
"redux",
"hot",
"reload",
"hmr",
"live",
"edit",
"webpack"
],
"author": "https://github.com/anorudes, https://github.com/keske",
"license": "MIT",
"devDependencies": {
"autoprefixer": "6.5.3",
"axios": "^0.15.3",
"babel-core": "^6.4.5",
"babel-eslint": "^7.1.1",
"babel-loader": "^6.2.1",
"babel-plugin-import": "^1.2.1",
"babel-plugin-react-transform": "^2.0.0",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-polyfill": "^6.3.14",
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-preset-react-hmre": "^1.0.1",
"babel-preset-stage-0": "^6.3.13",
"bootstrap": "^3.3.5",
"bootstrap-loader": "^1.2.0-beta.1",
"bootstrap-sass": "^3.3.6",
"bootstrap-webpack": "0.0.5",
"classnames": "^2.2.3",
"css-loader": "^0.26.4",
"csswring": "^5.1.0",
"deep-equal": "^1.0.1",
"eslint": "^3.4.0",
"eslint-config-airbnb": "13.0.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^3.0.1",
"eslint-plugin-react": "^6.1.2",
"expect": "^1.13.4",
"exports-loader": "^0.6.2",
"expose-loader": "^0.7.1",
"express": "^4.13.4",
"express-open-in-editor": "^1.1.0",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.9.0",
"gapi": "0.0.3",
"history": "^4.4.1",
"http-proxy": "^1.12.0",
"imports-loader": "^0.6.5",
"jasmine-core": "^2.4.1",
"jquery": "^3.1.0",
"jwt-decode": "^2.1.0",
"karma": "^1.2.0",
"karma-chrome-launcher": "^2.0.0",
"karma-mocha": "^1.1.1",
"karma-webpack": "^1.7.0",
"less": "^2.7.2",
"less-loader": "^2.2.3",
"lodash": "^4.5.1",
"material-ui": "^0.16.4",
"mocha": "^3.0.2",
"morgan": "^1.6.1",
"node-sass": "^3.4.2",
"postcss-import": "^9.0.0",
"postcss-loader": "^1.1.1",
"q": "^1.4.1",
"qs": "^6.1.0",
"rc-datepicker": "^4.0.1",
"react": "^15.3.1",
"react-addons-css-transition-group": "^15.3.1",
"react-bootstrap": "^0.31.0",
"react-calendar-component": "^1.0.0",
"react-date-picker": "^5.3.28",
"react-datepicker": "^0.37.0",
"react-document-meta": "^2.0.0-rc2",
"react-dom": "^15.1.0",
"react-forms": "^2.0.0-beta33",
"react-hot-loader": "^1.3.0",
"react-loading-order-with-animation": "^1.0.0",
"react-onclickoutside": "^5.3.3",
"react-redux": "^4.3.0",
"react-router": "3.0.0",
"react-router-redux": "^4.0.0",
"react-tap-event-plugin": "^2.0.1",
"react-transform-hmr": "^1.0.1",
"redux": "^3.2.1",
"redux-form": "^6.0.1",
"redux-logger": "2.7.4",
"redux-thunk": "^2.1.0",
"resolve-url-loader": "^1.4.3",
"rimraf": "^2.5.0",
"sass-loader": "^4.0.0",
"style-loader": "^0.13.0",
"url-loader": "^0.5.7",
"webpack": "^1.12.11",
"webpack-dev-middleware": "^1.5.0",
"webpack-dev-server": "^1.14.1",
"webpack-hot-middleware": "^2.6.0",
"webpack-merge": "^1.0.2",
"yargs": "^6.5.0"
},
"dependencies": {
"ant-design-pro": "^0.3.1",
"antd": "^3.0.0",
"lodash": "^4.17.4",
"prop-types": "^15.6.0",
"react-bootstrap": "^0.31.0",
"redux-devtools-extension": "^2.13.2"
}
}
Here's my current setup so you can understand how the build is being performed.
ROOT
├──/application
│ ├── models.py
│ ├── app.py
├──/static
│ ├──/bin
│ ├──/dist
│ │ ├──bundle.js
│ ├──/node_modules
│ ├──/src
│ │ ├──/actions
│ │ ├──/components
│ │ │ ├──/examplecomponenthere
│ │ │ │ ├──index.js (for example component)
│ │ ├──/constants
│ │ ├──/containers
│ │ ├──/reducers
│ │ ├──/store
│ │ ├──/webpack
│ ├──index.html
│ ├──package.json (the true one)
│ ├──server.js
├──/tests
├──config.py
├──index.py
├──main.py
├──package.json (one to help heroku start)
├──procfile
├──requirements.txt.
├──setup.py
├──tests.py
Here is my server.js file (unsure if this is needed):
const http = require('http');
const express = require('express');
const httpProxy = require('http-proxy');
const path = require('path');
const proxy = httpProxy.createProxyServer({});
const app = express();
app.use(require('morgan')('short'));
(function initWebpack() {
const webpack = require('webpack');
const webpackConfig = require('./webpack/common.config');
const compiler = webpack(webpackConfig);
app.use(require('webpack-dev-middleware')(compiler, {
noInfo: true, publicPath: webpackConfig.output.publicPath,
}));
app.use(require('webpack-hot-middleware')(compiler, {
log: console.log, path: '/__webpack_hmr', heartbeat: 10 * 1000,
}));
app.use(express.static(path.join(__dirname, '/')));
}());
app.all(/^\/api\/(.*)/, (req, res) => {
proxy.web(req, res, { target: 'http://0.0.0.0:8081' });
});
app.get(/.*/, (req, res) => {
res.sendFile(path.join(__dirname, '/index.html'));
});
const server = http.createServer(app);
server.listen(process.env.PORT || 8080, () => {
const address = server.address();
console.log('Listening on: %j', address);
console.log(' -> that probably means: http://0.0.0.0:%d', address.port);
});
Conclusion
One final note that on local I typically start the python side with a manage.py runserver
script. Then I open up another terminal, cd into static and do npm start
.
I'm so frustrated in trying to get heroku to work here for this multi buildpack. Any help would be so greatly appreciated! Even if it's a tutorial where I can learn more and start to dissect the problem or maybe I"m approaching this production setup incorrectly?
Upvotes: 0
Views: 530
Reputation: 5261
You should not have npm start in a postinstall script. You want to run your node.js server every time your web dyno restarts, not only every time your app is installed.
Furthermore, for Heroku, you should run the build of your "static" component in a heroku-postbuild script, not in a postinstall script.
Other than that, you need to make any build dependencies (such as webpack etc.) available to Heroku, either by setting config var NPM_CONFIG_PRODUCTION to false, or by moving them from "devDependencies" to "dependencies".
For more info see here.
Upvotes: 1