Hey folks,
Thanks for create-react-app! I'm new to the world of React and overall I've had a breeze getting set up. I'm a bit puzzled on deployment still, so I thought I'd open an issue to clarify these instructions in the How-to recipes section as suggested here.
Summary:
But I can't get 1 and 2 working together, i.e. a react client + node server working on a single Heroku app.
Github repo that repros the issue:
Questions:
Would appreciate any help. Thanks!
Hello, @vrk!
Basically this tutorial is based on the idea, that you open two different ports on the same instance.
So, it's ok when you doing this on your local machine or VPS, but it will not work with _heroku_, where dyno give you possibility expose only one port to outside world and map it to <app_name>.herokuapp.com:80
.
So, if you want to use _heroku_ for hosting react app and backend, I guess, you will need two separate apps, so it's Option 1 from your question.
Also there is workaround, you can serve your react app dist
directory from node
server using express.static, for example, and than the whole app will be served on the same port.
Oh awesome! Thanks for the fast reply. I'll give that express.static thing a try in the next day or two and will report back!
Well that went a lot faster that I thought. Thank you, got it working now! That's just what I needed to know.
For posterity in case someone else comes across this issue, what I did was the following:
app.use(express.static(path.join(__dirname, 'client/build')));
to my server.js"install": "cd client && npm run build && cd .."
under "scripts"I'm trying to decide if there's a tutorial I can contribute to the User Guide section of this project , maybe simply an addendum to the Integrating with a Node Backend section that explains the changes needed if someone wants to deploy that to Heroku. I'll keep this open for now for this reason.
@vrk Can you please update your fork of food-lookup-demo to include what you did to get it properly deployed on Heroku? I followed the steps in your fork as well as what you said in this comment and I still can't access the api dyno. I get 500 Internal Server Error.
Never mind, I got it deployed on Heroku using a server that serves the static build.
For anyone else who wants to deploy the original 'create-react-app with a server example' (https://github.com/fullstackreact/food-lookup-demo) to Heroku:
"react-scripts": "0.2.3"
from _devDependencies_ to _dependencies_"start": "nf start -p 3000"
with "start": "node server.js"
"server": "API_PORT=3001 ./node_modules/.bin/babel-node server.js
with "install": "cd client && npm install && npm run build && cd .."
const path = require('path');
process.env.API_PORT
with process.env.PORT
app.use(express.static(path.join(__dirname, 'client/build')));
Now when you deploy to Heroku, node will start your server and the client dependencies including react-scripts
will also be installed before it is run to build your static React app files.
Maybe worth including this into that guide?
cc @acco
@CaliAlec Awesome, glad you got it working!
@gaearon / @acco One thing that wasn't obvious to me until later: the fact that you don't have to/shouldn't run a server for your React apps made through create-react-app, and that the server launched through create-react-app is strictly for debugging.
I think it'd be helpful to mention that more clearly in the following places:
Feel free to do what you want with those suggestions, including ignore them :) I'm also happy to help with a PR for README.md.
@gaearon Good idea, time has come to add a Heroku/deployments section to the post.
@vrk Thanks for starting this discussion and for all the info! There are two main approaches developers will take here. The first is along the lines that you took — have your node server serve your static assets (the result of npm run build
). In that scenario, as you illustrated, you would have to have pretty different setups between development and production.
However, the production deployment we had in mind was a little different. You can use npm run build
to generate your static bundle and then toss that onto any static asset server, like S3. Then, you'd deploy your Node/API server to a service like Heroku. example.com
would point to index.html
on S3 and then the React app would make API requests to something like api.example.com
which refers to Heroku. This is more of a "pure" separation of client and server and would mean that the setup for production is closer to that of development.
Some hurdles that come to mind with that approach: Handling CORS if you don't have a domain name (using Heroku free tier) and making the dual target deployment strategy palatable. In any case, I'll drop a note here when we've expanded our post to include deployments.
@acco I think a lot of quick (small) projects probably don't bother with a static asset server split from the app server. Especially if you're going to deploy to some fast PaaS setup. Furthermore, handling multiple server processes just makes everything much more complicated if you're going to run things through an end-to-end test or something.
So, I sorta think supporting a single app server that handles client + server is a better developer experience even if in a more disciplined prod deployment, most people would want to get a static app server up and going.
With that in mind, what about making react-scripts expose a middleware that's analogous to react-scripts start
?
The method suggested here https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/ (which is linked from the official docs) where you create a nested node.js feels more complicated than necessary. I bet it also would break a lot of auto-detect code in common infrastructure like Travis CI and its default recipes. For example, Travis CI's caching documentation for node.js suggests manually specifying the node_modules
directory for cache. With the nested package setup, you'll never cache the client node_modules
.
If instead we exported a middleware, similar to how WebpackDevServer
exports one in addition to the CLI version, you can just use()
it from express.
I imagine a pseudo code looking something like:
const express = require('express');
const reactScripts = require('react-scripts');
app.use('/', reactScripts.createClientRouter());
app.use('/api', myapi_router);
Followed by a modification to package.json so that start no longer directly calls react-scripts start
.
This way a fullstack eng could hook into the react-scripts environment programmatically without breaking hermeticity.
Thoughts?
I don’t think we’re at the point where we’re ready to expose any APIs from react-scripts
yet.
@gaearon understood about the dangers with that, but:
(a) this is a fairly narrow API since the semantics are just "mount the equiv of react start on this path"
(b) without it, you have a fairly common use case (monolithic server that combines api + asset serving) that requires some moderately complicated workarounds, or ejecting.
I feel like it might be worth considering bumping the priority a bit.
Thanks for all the info and replies!
@acco: Makes sense. Thanks, and looking forward to seeing what you come up with!
(b) without it, you have a fairly common use case (monolithic server that combines api + asset serving) that requires some moderately complicated workarounds, or ejecting.
My drive-by opinion: I agree with @awong-dev. Recommending a deployment solution that would involve 2 Heroku instances (or S3/Heroku combo, or some other pure separation) seems to go slightly against the "simple setup!" spirit of create-react-app. It seems it'd be nice if you could support a way for create-react-app to work harmoniously on a simple monolithic server. (But of course, these things are super easy for me to say as a drive-by observer of this project 😜 )
Hey @awong-dev @vrk —
Thanks for the thoughts!
Totally valid points. I do think it would be great if a developer could avoid working with both S3 and Heroku to deploy a create-react-app app. S3/credentials/s3cmd are all a pain to setup for the first time. There is a Heroku integration that alleviates most of this (Bucketeer) but it's not free.
For a simple, free, one-dyno setup, having your "API" server also serve static assets is the only option (as @vrk highlighted). So it just means there will be some divergence between the setup of your app in development vs production. (You could close this divergence by having your Node+Express server proxy requests to Webpack in development (as opposed to the other way around) but I need to think through if this is more or less confusing.)
In any case, I'll compose examples for each of these scenarios and see what will be reasonable to suggest. Hopefully next week. I'll comment here when I have something
@acco Hi have you come up with anything that works now?
Hi @crazyyi Thanks for pinging me :) Full intention to do this still. I'll post here when I make time for it
Hey all — Thanks for your patience!
I pushed a solution along with a quick write-up to food-lookup-demo
. Per my earlier comment, this solution uses the free "one-dyno" solution. We have Node+Express (server.js
) serve the static bundle (client/build
) alongside the API in production.
I still need to write the blog post that builds off the previous one, describing the steps for readying that repo for deployment. In the meantime, the diff is highly informative. Notably:
start
and run dev
, respectively).client/build
into git (so we can push it to Heroku).Outside of that, I just did some juggling with the PORT
environment variable to make everything work OK with Heroku.
Hope this helps!
@acco Is there any reason why you have decided to run the server in production with babel-node? Babel docs recommends against this.
@faurehu There isn't other than to keep things simple :) Indeed, transpiling server.js
with babel before deploying would be better for a real production app
Another, IMHO easier way to make this deployable on heroku without buildpack.
heroku create
heroku config:set NPM_CONFIG_PRODUCTION=false
npm install express --save
+ create simple server.js static server, like this one: https://github.com/heroku/node-js-getting-started/blob/master/index.js . Just rename the 'public' to 'build'echo 'web: node server.js' > Procfile
"postinstall" : "npm run build",
git push heroku master
Works like a charm, builds the project on heroku, not locally.
For folks still looking to run create-react-app and express together on heroku, I wrote this article that covers how we are doing it at the moment.
https://originmaster.com/running-create-react-app-and-express-crae-on-heroku-c39a39fe7851
For anyone considering deployment on Heroku with separate instances for server and client and using cookies, keep in mind that herokuapp.com
is on the public suffix list meaning that you can't pass cookies directly between two Heroku instances:
https://devcenter.heroku.com/articles/cookies-and-herokuapp-com
A custom domain url bypasses the issue, depending on DNS setup.
Is there anything actionable for us in this issue? Perhaps, somebody would like to add something to the Heroku deployment section in User Guide?
Hello @acco !
After being at this for hours, and looking at several different implementations, your's actually worked! Particularly in removing "/build" from the .gitignore file as per your food-lookup-demo, I'm able to run the build process in my dev environment and just push the entire build folder to Heroku (and not to mention my git repo).
Since it's been about 7 months since you posted that implementation (and things move so fast in the react community :-) ), do you still use this implementation or has your perspective changed on this? Is it still good practice to remove "/build" from the .gitignore in the interest of preserving simplicity when deploying to Heroku?
Let me know.
hey guys, looking through this I'm still struggling a bit to run it the way I need:
1) client + server.js hosted on heroku, which makes request to:
2) a separate api server.js also hosted on heroku
Particularly, I want my client-server to make requests for user data to my api-server on all routes. Is this possible..?
I'll close as I don't see an action item here for us.
GLAH!!!!! BWAHHH! (thats a happy scream) Sorry, I was stuck on my own slightly different problem with this forever!!!
Thank you @vrk and @CaliAlec for this issue.
While slightly different, fixing my scripts to this based off of your responses helped me fix my issue!:
"scripts": {
"start": "node server.js",
"client": "cd client && npm run start",
"server": "nodemon server.js",
"install": "cd client && npm install && npm run build && cd ..",
"dev": "concurrently --kill-others-on-fail \"npm run server\" \"npm run client\""
}
Also, I used es6 for the path for a slightly cleaner look. app.use(express.static(
${__dirname}/client/build));
Thank you! I hope if there is anyone else with my problem they see this post and see my fix (or yours!)
If anyone is trying to figure out how to do this with a Rails backend, I suggest checking out this blog post: ReactJS + Ruby on Rails API + Heroku App
Seems like the Rails version of this has been neglected. 😞 Maybe a deployment section can also be added to the blog post and readme for others to find.
Most helpful comment
Hey all — Thanks for your patience!
I pushed a solution along with a quick write-up to
food-lookup-demo
. Per my earlier comment, this solution uses the free "one-dyno" solution. We have Node+Express (server.js
) serve the static bundle (client/build
) alongside the API in production.I still need to write the blog post that builds off the previous one, describing the steps for readying that repo for deployment. In the meantime, the diff is highly informative. Notably:
start
andrun dev
, respectively).client/build
into git (so we can push it to Heroku).Outside of that, I just did some juggling with the
PORT
environment variable to make everything work OK with Heroku.Hope this helps!