How can I pass a custom server hostname using React? - reactjs

I want to be able to pass a custom server hostname when running my React app to be used in the URL when I need to fetch data. The server is currently running on my local computer, so when I use fetch(<URL>).
I've been using "http://localhost:...." which has worked perfectly. But I want to be able to have to pass a custom host name, such as my IP address, to be used in the URL (i.e., http://ipaddress:...).
I've tried starting my app like this:
serverhost=ipaddress npm start
And then in my package.json file
"scripts" : {
"start": "react-scripts start $serverhost"
}
And in file start.js I can access process.env.serverhost, but I want to be able to access the hostname in the browser for my actual fetch calls. I don't want to set the hostname in "config" in package.json file or in an .env file, because it has to be able to change (I'm under the impression that this isn't possible). I just want to be able to access the server hostname given as an argument in the command line in my source files.
(I read somewhere about doing
REACT_APP_MY_VAR=test npm start
and then accessing it as
process.env.REACT_APP_MY_VAR
in the source files, but when I tried to fetch(<URL>), I got a bunch of failure to parse URL errors.)
I tried using REACT_APP variables and I no longer got parsing URL errors when fetching.

You can have .env file per environment and then set
REACT_APP_API_ENDPOINT=http://localhost.whatever
So for example .env.development
REACT_APP_API_ENDPOINT=http://localhost.whatever
.env.production
REACT_APP_API_ENDPOINT=http://production.whatever
Usage of env variables is explained here https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#what-other-env-files-can-be-used
Eventually, you can add it to your script like
"scripts" : {
"start": "REACT_APP_API_ENDPOINT=http://localhost.whatever npm/yarn start"
"start-production": "REACT_APP_API_ENDPOINT=http://production.whatever npm/yarn start"
}
If you don't really want to use above approaches at all then, assign your ip to some variable and add it to command to run your app
"scripts" : {
"start": "REACT_APP_API_ENDPOINT=$(curl ifconfig.me) npm/yarn start"
"start-other: "REACT_APP_API_ENDPOINT=$(echo MY_VAR_WITH_IP) npm/yarn start"
}
And then access your url from process.env.REACT_APP_API_ENDPOINT

You can set a base tag in your index.html that would set the base for all your requests from there on.
You can create the base tag dynamically by reading the document.location and map your required path.
Note that all relative paths will be then mapped from your baseTag
An example would be something like this
<base href="https://www.dog.ceo/" />
And then create a request like this
fetch('api/breeds/list/all')
.then( response => response.json() )
.then( ({message}) => console.log( message ) );
The full address of that call would the be https://www.dog.ceo/api/breeds/list/all

Related

React not fetching data from the updated env file

There are some environment variables in my .env file that gets updated when some values get updated on the database.
Example:
REACT_APP_FIREBASE_ID=1234567890
When I log this to the console on my react app:
console.log(process.env.REACT_APP_FIREBASE_ID) //this gives "1234567890"
But when the .env file is updated with something else:
Example:
REACT_APP_FIREBASE_ID=9876543210
The log still gives the old value:
console.log(process.env.REACT_APP_FIREBASE_ID) //this still gives "1234567890"
I am using on CRA on dev mode with "npm start"
If I terminate the server and restart it again, I am able to see the correct output to the console.
BUT, this doesn't work after "npm run build"
How can I clear the environment cache after the .env file is changed on the Production Mode?
This is how I solved it.
My first approach was to get the value from the .env file, but unless the server is restarted, this approach doesn't work. [BAD]
My second thought was to create a JSON file and keep the data from inside that, and update the JSON file whenever the Database is updated.
Sadly, this was a very bad approach because CRA (or probably any react project) cannot call a file outside the src folder and after the build, there is no scope to change the built js files. [VERY BAD]
Finally, I managed to solve this using this approach [DECENT, I Guess]
Saving the data on a table in a key-value format (You may just save in JSON format too)
When the application is loaded (main component is loaded), call an API that gets that key-value(or JSON) and store the data in the LocalStorage of the browser.
Get the value on the application with localStorage.getItem("theKeyName")
A Tip:
You could check if the key is already present on the LocalStorage before making the API call.
if(localStorage.getItem("theKeyName") !== null) {
//your API fetch request or redux dispatch
}

How to modify local url by default

I'm newbie in react and I would like to know if it's possible to modify the url by default http://localhost:3000 to http://whatever.local:port_number where port_number could be whatever port.
After install react, I have modified my package.json and add this line before dependencies:
"proxy": "http://localhost.example:3000/"
But when I start the project with: npm start the url opened is http://localhost:3000 and not http://localhost.example:3000.
What can I do that my project start with http://localhost.example:3000 instead of http://localhost:3000?
Edit I:
I have created a .env file with HOST=http://local.example:3000 and I've got this error:
Edit II:
It doesn't work to me :(
.env file
hosts file
ping to new host
You can create an .env file with the key HOST=localhost.example it should solve your issue.
in your package.json after name or version, you need to set "homepage":"http://localhost.example:3000" (it wouldn't work !),
but if you wanna change your url after http://localhost:3000/.... you should write like: "http://localhost:3000/home (or your favorit name)"

How to inject API server URL when deploying react frontend?

Disclaimer: I am a React noob so perhaps what I am trying to do is not the React Way
I am writing a React front-end that will be deployed to be served statically by some cloud provider, e.g. S3 or Google Storage or whatever. This front-end interacts with several API servers that live somewhere in the cloud, maybe in the same provider, maybe not. Furthermore, while developing the UI or part of it, those servers' addresses might be local or testing instances.
How do I inject the API server(s) URLs into my react application in a flexible so that I can deploy in dev, staging or prod using different addresses?
SOLUTION: I finally ended up using a combination of solutions proposed:
use .env.production and .env.development files (exact names) to store the variable REACT_APP_API_URI = 'host'
this is automatically picked-up by create-react-app's build scaffolding and available in UI code as process.env.REACT_APP_API_URI
Note this somewhat goes against principles from 12 Factor Apps, e.g. storing env variables in files in version control but it does the job ATM.
You can try this:
// http.js
const getBaseUrl = () => {
let url;
switch(process.env.NODE_ENV) {
case 'production':
url = 'https://stackoverflow.com';
break;
case 'development':
default:
url = 'https://google.com';
}
return url;
}
export default axios.create({
baseURL: getBaseUrl(),
});
Using this package https://github.com/toddbluhm/env-cmd you could create an env file for your environment
for example create .env.staging and .env file with this code
// .env.staging file
API_URL=https://staging.url.com/api/
// .env file
API_URL=https://url.com/api/
How to fetch with API_URL from env variable:
fetch(process.env.API_URL)
Then you can just add some extra scripts in your package.json:
{
"scripts": {
"build:staging": "env-cmd .env.staging yarn build",
"build:prod": "env-cmd .env yarn build"
}
}
You can use .env file if the API's are constant for development or production environment. after build you can't change these parameters.
If you want to change the URL after build, add a js file lets say config.js
Include the conf.js in index.html
Add URL in conf.js like
var URL1 = 'https://www.google.com'
You can access the parameter like :
export const {URL1} = window;
You can do that making use of environment variables on the build step for example.
You can use something like .env that allows you to define environment variables and load them on your webpack file for example (assuming you use webpack). But you can really use it with any bundler.
.env file:
API=http://localhost:3000
On your webpack you could make use of the DefinePlugin
example taken from docs: add your API env
...
require('dotenv').config()
...
new webpack.DefinePlugin({
API_ENDPOINT: process.env.API,
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify('5fa3b9'),
BROWSER_SUPPORTS_HTML5: true,
TWO: '1+1',
'typeof window': JSON.stringify('object')
});
Anyway, this is just one way. I like this way because it makes my project ready for defining API keys and other useful stuff for different environments.
NOTE: You can even define different .env files for local, staging and production and load the respective one in the webpack depending on the build type.

adding ssl to my react-app to my heroku server

I am trying to add ssl to my heroku server using certbot.
However, the issue I am finding is that I need to create a url to verify my domain -- which is the issue as I cannot seem to add both my server code as well as my client side code to the same heroku server (and get them to both run at the same time which I need to verify the domain..)
Try this:
Begin a certbot manual certificate: sudo certbot certonly --manual
Follow the prompts until you get to the part that says to make a file available at a specific like like yourdomain.tld/.well-known/acme-challenge/longstring123 Do not press Enter to Continue! Take note of both the URL and the "file" data it gives you.
In your server.js, add something like the following:
app.get('/.well-known/acme-challenge/your-long-url-string', function(req, res) {
res.status(200);
res.send('your-long-verification-file-data');
});
Make sure that this Express route handler is above the one you use for React.
I had a similar problem and spent hours trying to figure this out today. My problem was I kept getting a 404 error when trying to access .well-known/acme-challenge/{verification_file}.
The problem wasn't React or Heroku, the problem was that I was using serve -s build (package serve-handler) to serve my app, this is an static server and it won't let you fetch files without an extension. There might be a way to configure it, but I don't know how. Maybe setting headers... Anyhow, I tried files with .txt and it works, but without an extension it redirects to 404.
So instead removed my static server and added ExpressJS to serve the app instead.
To accomplish this:
Step 1
In your project folder run:
npm i express path
Step 2
Create a file in your project folder called server.js and add this code to it. This code will initialize and serve your project.
const express = require("express");
const path = require("path");
const app = express();
app.use(express.static(path.join(__dirname, "build")));
app.get("/*", function (req, res) {
res.sendFile(path.join(__dirname, "build", "index.html"));
});
app.listen(process.env.PORT || 80);
Step 3
If your package.json file under scripts make sure Heroku will initialize your project correctly when deployed:
"scripts": {
...
"start": "node server.js",
...
}
This should do it. Now your project is served by a dynamic server and it will delivery files without an extension. You can remove your serve package now if you aren't going to use it anymore.
Reading
Couple links that might be useful:
https://create-react-app.dev/docs/deployment/
https://expressjs.com/en/starter/hello-world.html

Multi Tenant App in React

I'm building a multi tenant app in React (with Webpack setup via base, dev and prod config files), and I'm wondering the best way to create and access per-tenant variables.
When I run my app with:
npm run start tenant1
I am able to access tenant1 in Webpack by using this:
const tenant1 = process.argv[process.argv.length -1];
However, now I'm wondering what is the best way to make that variable globally accessible. My hope is to use that variable to the create a folder structure within the app along the lines of:
/app/${tenant}/img/
/app/${tenant}/css/
/app/${tenant}/components/
Ideally without having to import a variable into every single javascript file.
Any suggestions or links to existing setups would be greatly appreciated.
Update Jan 2019:
I've found a way to achieve this with Create-react-app, not perfect but it works and achieves the following:
Run a React app on port 3000 that works for multiple domains simultaneously.
Forward all requests not handled by React to a back end.
Optionally use SSL in development.
Create-react-app has a proxy option that is very easy to setup. Simply add the following line to your package.json file:
"proxy": "http://localhost:5000"
However, this will not work for multiple domains. There is a more advanced proxy configuration available.
After following these steps, you will be able to control where different requests are sent, but it does not entirely provide the ability to proxy multiple domains - to achieve this:
Create the file .env in the root of your create-react-app project.
Add the following to it:
NODE_PATH=src/
DANGEROUSLY_DISABLE_HOST_CHECK=true
# optionally add this line for SSL in development
HTTPS=true
From the advanced proxy instructions above, you should end up with a file called setupProxy.js in the root of your /src folder - change to the following:
const proxy = require('http-proxy-middleware')
const options = { target: 'https://[::1]:8000', secure: false }
module.exports = function(app) {
app.use(proxy('/api', options))
app.use(proxy('/graphql', options))
}
The magic part is the https://[::1]: 8000, which will forward all domains from the root request to the same back end. This doesn't seem to be well documented anywhere, but I believe it is the IPv6 equivalent of 127.0.0.1.
After this, you can add entries to your hosts file (for example: 127.0.0.1 some-domain.example.com), and in your React app use just the paths (/api or /graphql) and requests should be proxied to the same domain as the browser is running on.
Original answer:
I ended up taking a fairly manual approach to this.
I'm using a react/redux boilerplate, but I've modified the npm scripts like so:
"start:tenant1": "cp -r ./tenants/tenant1 ./app/tenant && cross-env NODE_ENV=development node server",
"start:tenant2": "cp -r ./tenants/tenant2 ./app/tenant && cross-env NODE_ENV=development node server",
Each tenant folder is copied to the app when the development server is run with the relevant command, and files are named the same within each tenant folder (masthead.svg, vars.js, etc) so that imports throughout the app can be static.
Mostly this works because I'm not using a node server in production, a static build folder is generated by the boilerplate.
Hope this helps someone.

Resources