Base URL Discrepancy Between Localhost and Deployed - create-react-app - reactjs

I am building a web application from the ASP.Net Core React template, whose "ClientApp" React portion appears to be a 'create-react-app' project. When I deploy the app to IIS, it gets hosted in a subfolder from the root, i.e. http://myserver.com/SubApp. Because of this, I have changed the "homepage" value in package.json to http://myserver.com/SubApp.
What I am now experiencing is that when I am making fetch calls in my javascript code locally, if I use fetch('/myendpoint'), the the url requested locally is https://localhost:44315/myendpoint (which works), but when deployed, this url becomes http://myserver.com/myendpoint, which does not work.
Conversely, when I make the endpont fetch('myendpoint') (no leading slash), the server requests the correct URL http://myserver.com/SubApp/myendpoint but localhost fetches the incorrect URL, https://localhost:44315/SubApp/myendpoint.
I understand that the leading slash makes the request from the root, but the root appears to be different in localhost vs. on the server.
For local debugging purposes, I tried setting a proxy in package.json to https://localhost:44315 so that hopefully fetch('myendpoint') (no leading slash) would work in my local environment, but when I try this, chrome prompts me to sign in repeatedly without ever successfully making the request.
I feel like I am almost there, but I must be missing something. How can I configure my package.json (or other project configuration) to make the fetch commands succeed on both localhost and my server, without doing something hacky like checking the URL in the javascript code before every fetch?

What you need is Proxying API Requests in Development.
It easily allows you to call any endpoint in development and forward it to your backend or other location without CORS issues or your problem of mismatched endpoints.
You will have to use the manual configuration option, since the default proxy setup only helps with host/port forwarding.
Install http-proxy-middleware as a dev dependency:
$ npm -i http-proxy-middleware --save-dev
Following the guide linked above, you can use this configuration:
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/SubApp/*',
createProxyMiddleware({
target: 'localhost://44315', // or anything else, point to your dev backend
pathRewrite: {
'^/SubApp': '/', // remove base path
},
})
);
};
You can then fetch with the /SubApp part in the request which will be removed in developement. You can use other configurations to achieve this, this is one example.

I had this same experience as well and found that setting the homepage value in the package.json is a dead end. What you want to do is make use of the PUBLIC_URL environment variable to set a full absolute url in your .env . There may be a way to use relative urls, but there were some edge cases that just made it cleaner to use an absolute URL.
When you set this environment variable you want to make use of it in the following places:
Your Routes (if you use react router)
The path should be prefixed by process.env.PUBLIC_URL
Your internal links should also be prefixed by the env var.

Related

React App doesn't make proxy calls after deployed to azure

Simple setup:
React App created with create-react-app
ASP.NET Core web API - a couple of controllers (currently no security until I make it work)
Both the API and Application are deployed to Azure.
When I run the app locally with configured proxy (I contact the deployed API on Azure) it works correctly makes the calls.
If I try the API directly from my machine it works too (PostMan for example)
When I open the deployed React APP - The application loads correctly but the call to the API doesn't get proxy(ed). What I mean it's not returning 404, 403 - it returns status 200, but makes the call to the app itself instead of proxy the request to the API.
I've tried both using "proxy" configuration in package.json as well as using "http-proxy-middleware". Both cases work with locally running app, but not deployed. Here is the configuration of the proxy:
module.exports = function (app) {
app.use(
'/api',
createProxyMiddleware({
target: 'https://XXXXXX.azurewebsites.net',
changeOrigin: true,
})
);
};
I suppose it's something related to the configuration of the node server used when I deploy to azure, but I don't have a clue what.
I've used the following tutorial for deployment: https://websitebeaver.com/deploy-create-react-app-to-azure-app-services
But as seen from content there is no proxy part in it.
After long troubleshooting I realize the issue was due to wrong understanding of the proxy configuration.
So I was not realizing the proxy configuration is respected only in debug mode and totally ignored into prod build. And of course while debugging the issue I was not realizing the Azure Deployment pipe was doing production build. So the resolution was to detect the production/dev on application layer and inject correct URL.
Here I hit another issue - process.env.NODE_ENV, which I was using to distinguish between development and production was undefined into my index.tsx - it was available in App.tsx and all its children, but not in index.tsx where my dependency container was initialized.
What resolved my issue is package called dotenv. Then I've just imported it into index.tsx and the process.env was available.
import * as dotenv from 'dotenv';

React CRA: When adding subdomain on localhost it says: The development server has disconnected

I am on a project with create-react-app without ejecting.
I wanted to have subdomains on localhost or a fake host for development.
When I added my host in windows hosts file it said invalid host header even if I add HOST=mydevhost.com DANGEROUSLY_DISABLE_HOST_CHECK=true in .env file.
I couldn't make it work without using third party apps so I used Fiddler and it worked as expected now the sites comes up but instantly says:
The development server has disconnected.
Refresh the page if necessary.
The problem is that the fast refresh doesn't work now and I have to refresh the site every time I make a change. Is there anything that I'm doing wrong here? Should I even use something like Fiddler here?
I ended up using react-app-rewired and in config-overrides.js I added the subdomain to allowed host. The final config looks like this:
module.exports = {
webpack: (config, env) => {
return config;
},
devServer: (configFunction) => (proxy, allowedHost) => {
const devServerConfig = configFunction(proxy, allowedHost);
devServerConfig.allowedHosts = ["subdomain.localhost"];
return devServerConfig;
},
};
I thing you can do that from your operating system to point your local domain to your react server, meaning that can create a local domain that points to the app server (host:port).
here's a guideline that may help:
https://www.interserver.net/tips/kb/local-domain-names-ubuntu/
Relevant answers:
How can I develop locally using a domain name instead of 'localhost:3000' in the url with create-react-app?

Deploying Create React App to a subdirectory but make API calls to parent path

I'm deploying my Create React App to a specific path in a larger non-React webapp. For example, I will say the webapp path is www.example.com and the React app is deployed at www.example.com/react/
I have done this by setting the "homepage" property in package.json of the React app to "homepage": "/react", which does properly serve the static files from the /react/ path on my server.
However, when I make API calls from my react app, they go to /react/api/etc instead of /api/etc.
I can configure axios to use a hardcoded base path of www.example.com, but I deploy this to multiple environments with different URLs and need a solution that doesn't rely on a hardcoded value.
I could also write a workaround on the server side, but it would be less clean / mess with my logging and request statistics.
I would love a clean solution if one exists.
what if you used the window.location property in your axios config object:
{
baseURL: `${location.hostname}/api/` // or window.location.hostname
}

How to inject port and host using ENV variable in Create React App Proxy settings?

Is it possible to set proxy settings in CRA differently than with package.json? For example using ENV variables?
From CRA docs it states
Use environment variables to inject the right server host and port into your app.
Is this sentence about proxy or dev server itself?
According to the this and this, the only way to influence proxy settings is via package.json.
Note: this feature is available with react-scripts#2.0.0 and higher.
You can manually configure proxy now. You need to install http-proxy-middleware first.
npm install http-proxy-middleware --save
Then create a file src/setupProxy.js with following content.
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api', // You can pass in an array too eg. ['/api', '/another/path']
createProxyMiddleware({
target: process.env.REACT_APP_PROXY_HOST,
changeOrigin: true,
})
);
};
You can add proxy now in the .env file, remember you need the REACT_APP_ prefix for development server to pick up the environment variable.
REACT_APP_PROXY_HOST=http://localhost:5000
Ref: Proxy manually
Regarding:
Use environment variables to inject the right server host and port
into your app.
This refers to neither the proxy server nor the dev server. This is a recommendation on how to handle per-environment server settings if proxy does not provide enough flexibility for you (i.e. if you need to use protocols other than http, https, or ws). For example, if you have an api call within your code, you can use an environment variable to do something like this:
axios(process.env.REACT_APP_BASE_URL + endpoint, options).then(data=>{ console.dir(data); })
Where each environment has it's own definition of REACT_APP_BASE_URL.
Without package.json:
It is not possible to influence proxy settings without modifying package.json.
See Configuring the Proxy Manually (CRA docs)
package.json is the only documented way to influence proxy settings.
You can also see line 96 of CRA's start.js. This implementation leaves absolutely no ability to use ENV vars to inject the host/port to CRA's proxy settings.
This is really one of the reasons I detest CRA, to be honest. All of this behavior is so very easily handled when not bound by the limitations of CRA.
You could just eject create react app:
npm run eject
All of these problems will go away instantly... The start.js script referenced above is a bit of the code that is ejected. So you would very easily be able to replace those proxy settings with ENV vars.
Just be certain to create a checkpoint for yourself prior to doing this as there is no reverting an ejection.
However--if you must stick with CRA--you CAN very easily modify package.json using environment variables.
How to inject ENV vars into package.json at build time:
This is essentially what CRA does with the REACT_APP_ env vars (though it directly injects them into process.env).
package.json is just a big JSON object, so all you need to do is parse it on the server side right before you deploy, update the "proxy" value to the ones provided by your ENV vars, save the file, and then run your build/deploy.
Here is a very simple example of how to achieve this, you just need to run this script before deployment in a context where you still have access to process.env:
const fs = require('fs');
// read/process package.json
const file = './package.json';
let pkg = JSON.parse(fs.readFileSync(file).toString());
// at this point you should have access to your ENV vars
pkg.proxy = `${process.env.HOST}:${process.env.PORT}`;
// the 2 enables pretty-printing and defines the number of spaces to use
fs.writeFileSync(pkg, JSON.stringify(file, null, 2));
I know this doesn't explicitly answer the question of how to handle this "without package.json", but I don't think that's the necessary approach anyways.
Further, this DOES asnswer the title question of "How to inject port and host using ENV variable in Create React App Proxy settings?"
Is it possible to set proxy settings in CRA differently than with package.json?
No. It is not possible. Well, at least not yet (2017/01/14).
The React proxy functionality is getting a bit complicated and as a result, the idea of setting up a proxy in a React app from env variables will be implemented as soon the simplification of the "advanced" proxy configuration is finished.
Is this sentence about proxy or dev server itself?
It Is about the dev server itself. All proxy configuration, as things stand, is configured in package file.
You can define env variable for port and host.
REACT_APP_PORT: "PORT of Application",
REACT_APP_HOST: "host of application"
To inject all the env to your application you can use webpack define plugin.
You need to add in plugins section like.
const env = require("path to your env file")
//other webpack settings
plugins: {
//plugins
new webpack.DefinePlugin(env)
}
Now if you see the code of create-react-app it looks in env variable first to find the port and host.
Also, note that
You MUST create custom environment variables beginning with REACT_APP_. Any other variables except NODE_ENV will be ignored to avoid accidentally exposing a private key on the machine that could have the same name

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