Backgroud
The backend is a Rails API-only app running on Heroku (on port 5000 during local development).
The frontend is made with create-react-app and is hosted in an S3 bucket behind AWS CloudFront (on port 3000 during local development).
Development Setup
Locally, the frontend's package.json includes:
"proxy": "http://localhost:5000",
This lets me run a Javascript command like…
await fetch(`/someEndpoint`)
…and the development server running on localhost:3000 forwards the response from http://localhost:5000/someEndpoint. This works great and my frontend processes the responses successfully 👍.
Production Issues
Now I'm running on production.
If I change nothing, the same line will try to load from https://asdfasdfasdf.cloudfront.net/someEndpoint which will 404.
Instead I want to configure the app so that in production it will hit the Heroku app https://my-awesome-rails-app.herokuapp.com/someEndpoint where the Rails API is running.
Questions
(1) Is what I want possible without changing the code
await fetch(`/someEndpoint`)
either by configuring the frontend to use my-awesome-rails-app.herokuapp.com, or by configuring CloudFront to forward some requests to S3 and others to Heroku?
(2) If neither are possible, how should I write the fetch() command to let me hit Heroku in production but still keep the local proxying behavior during local development? Some use of environment variables maybe?
In my opinion, It's better to follow the same process (to get the API hostname) in all the environments. Also setting up CloudFront only for this purpose is not convincing unless you have other reasons to do so.
I think you could use an environment variable and here is how you can do it.
using environment variables in react project
react-scripts supports environment variables using dotenv library.
we build the react app using:
REACT_APP_API_DOMAIN=REACT_APP_API_DOMAIN npm run build
or you can define a .env.production where the NODE_ENV environment variable is set to production.
REACT_APP_API_DOMAIN=http://localhost:5000
Then in your code file, you can access it from process.env
await fetch(`${process.env.REACT_APP_API_DOMAIN}/someEndpoint`)
Please read the reference, there is a pattern using which you should define the environment variables for it to work. for e.g each environment variable you define should start with REACT_APP_
Reference:
https://create-react-app.dev/docs/adding-custom-environment-variables/
hope this helps.
Related
I just deployed a React app on HEROKU (the app is made from create-react-app).
My app's file are on Github and I set up HEROKU to point to that github repo.
My app uses an API key. Normally (when I spin up the project locally) that API key is being accessed from an .env file at the root level and I access it with the dotenv package. However I'm not committing this .env file to github (.env is in gitignore).
I understand that HEROKU needs to know about this API key. So, I went to the settings page of my HEROKU app and added the API key there. However I'm not sure I'm doing this correctly since the API is not working.
This is how I'm entering the API key in HEROKU's settings (I've also tried this with just REACT_APP_LASTFM_API_KEY - not the process.env. before it):
And this is how the API key is being used in the app (the variable is process.env.REACT_APP_LASTFM_API_KEY)
const url = `http://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist=${artist}&api_key=${process.env.REACT_APP_LASTFM_API_KEY}&format=json`;
The environment variables declared in the Heroku settings are specific for processes that run on Heroku itself.
Since your app will run on a client's browser, the react app will look for those environment variables in the client's browser.
The only job of Heroku is to host & serve static files. These static files that Heroku serves must have these environment variables embedded in them & this is exactly what CRA react-script does for you.
Please know that this isn't specific to just Heroku, the same holds true for say Azure or Cloud run.
I've recently built a Nextjs app, which I am hosting on Google Cloud Run.
My app makes some requests to external APIs from the getStaticProps() method.
I would like to be able to point to a different API host depending on the environment (e.g prod or dev) using environment variables which would be set differently for each environment.
I know I can store these variables in environment specific files like .env.development and .env.production however I would like to be able to store these environment variables in the environment variables field in the Google Cloud console for the cloud run service and skip storing them in the files altogether.
I have tried adding in the variables to Cloud Run, but it does not work. I have also tried prefixing the variables with NEXT_PUBLIC_... With no luck.
Did anyone have any tips on how to accomplish this?
Ok... I think I have figured it out now.
I was using Cloud Builds to build my container, and the container runs npm run build before it runs npm run start.
I assume that my Cloud Run variables aren't available at the point in time when Cloud Build is building the project.
So, I think my solution is prob to try and inject the variables at the point when it is building, using substitution variables.
EDIT: Confirmed. If I start Nextjs in dev mode, such that the page is rendered on the server for each request, then the Cloud Run environment variables are used.
To build the Nextjs app for production, I include the environment variables in the Dockerfile that is built by Cloud Build
EDIT: as request, an example of a dockerfile setting the environment variable:
FROM node:16.13-alpine
RUN mkdir -p /usr/src
WORKDIR /usr/src
COPY . /usr/src
ENV NEXT_PUBLIC_MY_API_HOST='https://some.host.com'
RUN npm install -only=production
RUN npm run build
EXPOSE 3000
CMD npm run start
Then you can just reference the environment variable from within your code using process.env.NEXT_PUBLIC_MY_API_HOST
I start my React app using:
npm start
This starts the app on localhost:3000
I would like to have it start with a domain instead of localhost. When testing locally, I have a local domain (example mydomain.com) set to IP address 127.0.0.1. This allows me to use the actual domain in code when making requests to the backend. Without this, my code would need to support both localhost and my domain and swap them out in production.
npm start is only a command to run your react app in development mode.. for faster build and publish functionalities..
So for production, you will be using npm build to get a production build for the same. This won;t be automatically deplyed or hosted on your localhost:3000.
So production deployment you will have to host your compiled build files using IIS or some hosting application. There you can configure your preferred DNS, whcihever domain name you would want to use.
You can customise the url for deployment for development.
In your .env.development add this line
HOST='mydomain.com'
If you want to deploy at https, add this line also in same file
HTTPS=true
My current situation:
I have a jenkins pipeline to dockerize my node/express backend and build+dockerize my react frontend after every commit to github. This works so far. I am using docker and jenkins on ubuntu 18.
Problem:
My frontend (of course) can't connect to the backend when on the live server (because the route to the backend is http://127.0.0.1:8080. My first idea was to use environment variables but this is not working since react can't read env variables after built (because it's pure html/css/js). What are common solutions to this problem? I don't want to change the backend to the actual domain every time before I push to the repository and change it back to 127.0.0.1 to work on it again.
I researched some more and environment variables CAN be replaced by their value in build time (which is what I want) when you don't use an npm package like dotenv, but rather define variables that start with REACT_APP_.
More Information
"The environment variables are embedded during the build time" - should have read that before.
You can use env files to define different variables based on the environment
I'm using React with next.js and Google Cloud functions to serve the app. I also use firebase. I'm looking for the best way to automatically configure the staging and production configuration for the 3 environments.
Production: Uses production credentials
Staging: Uses staging credentials
Local: Also uses staging credentials
I have two Firebase projects and currently switch between the configuration using the firebase.js file in my app. I swap out the config object, then deploy. I want to be able to run the app locally, and both on staging and production without changing anything on deploy as it leads to errors.
The issue for me is setting different environment variables for the two cloud projects...I can read cloud environment variables I set up there, but only in the cloud functions environment, not in the client-facing app, which is where I am currently swapping the configuration.
I can see this being solved by:
Google Cloud Platform environment variables (but I have tried and failed to read them from the client). Maybe I change the next.js config to read something up in the cloud when running, instead of doing the config change at deploy?
Local nextjs environment configuration but this doesn't know anything about the two different servers (only dev vs prod which don't match up exactly with local and cloud)
React dotenv configuration (same as the point above)
webpack / npm configuration that swaps the config when compiling
Swapping env based on firebase use [environment] on the command line at deploy time
Points #1 and #5 seem the most likely candidates for automatic and seamless deployment but I can't figure out how to read the GCP config variables in React and for #5 I don't know how to run a custom script that could swap variables based on the firebase project currently being used on the command line.
Most info I have seen on this doesn't tackle this issue exactly - all the env variables are either only in the cloud functions or distinguish local vs cloud or dev vs prod, but cannot distinguish between two clouds and a local that uses the same config as one of the clouds.
Someone must have had experience with this?
I've finally tracked down an answer to this.
The best way I've found to handle this in React/nextjs is to use a combination of my ideas #4 and #5 from my original question.
As seen here, firebase has a firebase apps:sdkconfig command on the cli that:
Prints the Google services configuration of a Firebase App.
So knowing that, you can add a script to npm that creates a firebase config file each time the site gets built. Since it knows which firebase project you are currently using on the command line, it knows the version of the config to output when you're deploying.
Then, in your package.json, you can set it to run.
"scripts": {
...
"build": "npm run getconfig && next build",
"getconfig": "firebase apps:sdkconfig web --json > ./firebase-config.json",
...
},
Then, in your firebase.js or wherever you're configuring firebase in your app:
// Get the Firebase config from the auto generated file.
const firebaseConfig = require('./firebase-config.json').result.sdkConfig;
// Instantiate a Firebase app.
const firebaseApp = firebase.initializeApp(firebaseConfig);
This works both locally and in the cloud with no manual changes needed other than what you'd already be doing to switch the environment you're using (i.e. firebase use)
You might need to tweak which npm script it runs on or other small things to suit your needs, but this general setup is working great for me with production, staging and development environments.