Reputation: 31
I'm working on an app that uses React as the client with a Rails API as the server. I want to keep the client and server as separate as possible (right now they are in totally different repositories).
The React client was created using create-react-app, and currently calls out to an API using the proxy option in the package.json file, like so:
"proxy": "http://localhost:3001/"
Localhost 3001 is the location of my Rails API. Obviously this will not work in production.
How can I deploy this to Heroku while still letting the client have access to the server? Can I deploy two different Heroku apps and somehow have the client's app proxy requests out to the server's app? Or do I have to include the client inside of my rails project and deploy it all together?
Upvotes: 2
Views: 1770
Reputation: 3556
I took a different approach than Jimmy in doing this same thing.
I deployed two apps on Heroku: a Rails API and a Create-React-App front end. Without getting too specific, there are a few keys for setting this up. First, in your rails api, edit the cors.rb
file so that it allows cross-origin requests:
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'localhost:3000', 'https://myapp.herokuapp.com'
resource '*',
headers: :any,
methods: [:get, :post, :put, :delete],
end
end
As this file implies, my rails app does not run on localhost:3000
locally, i changed it to run on port 8000 instead by editing puma.rb
:
port ENV.fetch("PORT") { 8000 }
Create-react-app runs on localhost:3000
locally by default, so you can set your rails api to run on whatever port that you want, as long as it's different than your front-end.
I then made a file in my create-react-app that contains the API url and commonly used endpoints that I call AppConstants.js
:
// let APIRoot = "http://localhost:8000";
let APIRoot = "https://my-rails-api.herokuapp.com";
export default {
APIEndpoints: {
LOGIN: APIRoot + "/users/login",
SIGNUP: APIRoot + "/users/create",
TODOS: APIRoot + "/todos",
ITEMS: APIRoot + "/items",
},
};
Now you edit your fetch/ajax/xmlHttpRequest calls so that the URL it uses references these routes. For example using fetch:
fetch(AppConstants.TODOS, {
method: 'POST',
body: JSON.stringify(values)
})
.then(response => response.text())
.then((body) => {
console.log(body);
});
This app constants file makes it easy to switch between a local api root and a production api root.
Deploy your rails api to heroku as you normally would, a suitable build pack will automatically be detected. For your react app, I suggest using this buildpack as will make a production build of your create-react-app and serve the static assets for you.
Upvotes: 1
Reputation: 3255
I've done this before. I started out as you suggested in your question by building the react app and including it in my rails project and deploying the whole thing. This works just fine but I didn't feel like redeploying my back-end api every time I wanted to deploy my front-end app.
So I made a model called FrontEndAppVersion
in my rails app. I gave this model two columns:
I then had a minimal rails view render which would look for the active FrontEndAppVersion
and would link to the appropriate js and css files.
Over in my react app I used gulp to package up the app and push it to cloud front which is where my rails app linked to. In addition to sending the react files to cloud front, I had it make an api request to my rails backend app to let it know that there was a new version available and to make it the active one.
This might be overkill, but it also allowed me to rollback a version with ease. Instead of rolling back Heroku, I could rollback just the client side app by making a different FrontEndAppVersion
record the active one.
Either way you go, the proxy will need to change when you build it and if both apps are on the same domain, you can simply use /
.
Upvotes: 1