ReactJs Environment variables form NestJS - reactjs

We are working on a reactjs web application with NestJs for server-side.
At the moment we use ServeStaticModule.forRoot.
The app needs some configuration (e.g. url of analytics server, clientId, redirectUrl …).
Since Create React App doesn’t support server rendering, we added placeholders into the HTML and we want to inject variables to the client.
For example :
<html lang="en">
<head>
<script>
window.CONFIG = __CONFIG__;
</script>
https://create-react-app.dev/docs/title-and-meta-tags#generating-dynamic-meta-tags-on-the-server
How can we replace the CONFIG?

The following approach is for Express.
If you want to modify index.html then one approach is to use app.useStaticAsset(//build path, {index: false}) in main.ts to serve the content while ignoring index.html. Then you can sendFile(//index.html path) in root path to deliver a page.
To replace placeholder config, you can have something like readFileAsync in a respective service or controller to read index.html content and process it before delivering to a client.

Related

If I deploy my react app, will the link to a localhost still be valid, or will I also need to host the localhost app?

Basically the heading. I have a strapi app at localhost:1337 which I will fetch in React. I'm not very sure how localhost works, and therefore I want to know if the path will still be relevant when I deploy the react app.
When you deploy your react.js app on any server your url named http://localhost:1337/Dashboard
will be changed. In it http://localhost:1337/ is the base url or domain name. Which will change the server to the new one.
your code will maintain same value for that API and you will have to re-build your code each time you change your API, (most of people use low cost hosting provider which allow only port 80 to be used) my advice is to move your endpoit (backend url) outside your code in a json, .env file ... but what will work on most of platform is a variable defined in your public/index.html (not a best pratice but it will work) ex:
<html>
<head>
<!-- you will add this tag here it will contain your backend url -->
<script>
var bakendUrl = "http://....";
</script>
<!-- some other code here -->
</head>
<body>
<div id="root"></div>
</body>
</html>

How to set environment variable for a deployed Reactjs app on server?

I think my question is different from previously asked questions about environment variables. I know how to set the environment variables while building the ReactJs app. Once the app is built, it gives me the static files and i put it on server. This app works like a normal webapp.
Question:
If i want to give the client provision of changing one of the environment variables on a deployed build on server without opening the code files, how i can do that?
Note:
As this is not a specific coding question, please move this question to relevant SO forum if you think.
Thanks
It is not possible to change REACT_APP_XXXXX env. vars after compilation. They are getting "baked in" into the app permanently.
I solved this problem by having a dynamic JSON manifest file that is being served from the backend. The app loads the JSON file and reads its values. A variation of this approach is to have a Javascript js file served from the backend. The script executes a function which sets some variables in global window object.
Here is a snippet of a node express app that serves the manifest as JSON and as a JS file.
const express = require('express')
const app = express()
const manifest = {
appTheme: process.env.APP_THEME,
foo: "bar"
};
const cacheTimeoutSec = 600
class ManifestController {
static getJS(req, res) {
/// Generate IIFE function that sets window.serverManifest object
let fileChunks = [
'(function(){',
'var serverManifest=',
JSON.stringify(manifest),
'; window.serverManifest = serverManifest',
'})()',
].join('');
res.set('Cache-Control', `public, max-age=${cacheTimeoutSec}`);
res.setHeader('content-type', 'text/javascript');
res.write(fileChunks);
res.end();
}
static getJSON(req, res) {
res.json(manifest);
}
}
// Serve manifest in JS
app.get('/server-manifest.js', ManifestController.getJS);
// Serve manifest as JSON
app.get('/server-manifest.json', ManifestController.getJSON);
Option 1:
The react app manually fetches the JSON file from your backend (e.g. https://api.mybackend.com/server-manifest.json) and act on the data.
Option 2:
Include <script> tag in the html file header like so
<html lang="en">
<head>
<script src="https://api.mybackend.com/server-manifest.js"></script>
<title>Home</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>
If you put it in the <head> the browser will load the script and will execute it. The script sets the manifest in the global window.serverManifest object which the React app can access now at any time.

Making React aware of the custom configured CDN headers

Our React app is served from a static hosting using S3 and CloudFront.
We configured S3 and CloudFront to add CloudFront-Viewer-Country in the return header of each request made to resources in it. So for instance, our index.html makes a call to get the .js bundle from CloudFront, the returned header would include: cloudfront-viewer-country: US in my case.
My goal is to have the React app "wake up to life" already knowing the location of its user. I realize I can probably add some javascript to the index.html to keep/store it somehow so that the React root component can pick up on that and pass it on to wherever it needs to be (probably the redux state). But then I ask myself, how do I tap into the response header received when the <script> tag finished loading the bundle in order to extract the custom header from it?
the index.html is pretty straightforward. Its body looks like this:
<body>
<div id=root></div>
<script type="text/javascript" src="/myBundle.ac9cf87295a8f1239929.js"></script>
</body>
What do you recommend?
It isn't possible to access the headers from the page load or script load. You will have to make a separate request to access the headers.
You could also use browser's locale (navigator.languages) if you need this information for localization.

How to deploy React on IIS?

When working on localhost, the app is assumed to be on root of the local dev server
localhost:50001/index.html
But when deploying to a remote IIS server, there are other web apps running there, and each individual app must be created as an "Application" (IIS terminology)
So for example, the 'Default Web Site' is on port 80; other apps (also on port 80) have their own AppName.
MYSERVERNAME/App1/
MYSERVERNAME/App2/
MYSERVERNAME/MyReactApp/
So now to get to my React App i have an additional path
http://MYSERVERNAME/MyReactApp/index.html
The index.html produced by 'npm run build' contains absolute paths;
To make my deployment work, I manually edited the index.html to contain relative paths
So for example, instead of:
<script type="text/javascript" src="/static/js/main.d17bed58.js"></script>
I added a .(dot) in front of all paths to get:
<script type="text/javascript" src="./static/js/main.d17bed58.js"></script>
This works mostly, and all scripts load initially. BUT I am not happy with the result, because any links and client-side routes (i.e from react-router) that I click within the app, will revert to assume the app is hosted on the root of the webserver. i.e.
http://MYSERVERNAME/
http://MYSERVERNAME/Home
http://MYSERVERNAME/MyWorkOrder/
http://MYSERVERNAME/MyWorkOrder/123456
Furthermore, if I type any of the links directly on the browser (or refresh the page), it will fail obviously.
To recap. the question is I need to maintain the "true" path http://MYSERVERNAME/myReactApp at all times, when deploying to IIS. How to do that?
From the docs:
Building for Relative Paths
By default, Create React App produces a build assuming your app is hosted at the server root.
To override this, specify the homepage in your package.json, for example:
"homepage": "http://mywebsite.com/relativepath",
This will let Create React App correctly infer the root path to use in the generated HTML file.
For example:
<script type="text/javascript" src="/static/js/main.xyz.js"></script>
will become:
<script type="text/javascript" src="/relativepath/static/js/main.xyz.js"></script>
If you are using react-router#^4, you can root <Link>s using the basename prop on any <Router>.
More information here.
For example:
<BrowserRouter basename="/calendar"/>
<Link to="/today"/> // renders <a href="/calendar/today">
What i ended up doing
1) After npm run build, change absolute paths to relative paths within index.html (example href="./etc..." and src="./etc...")
2) use basename in <BrowserRouter basename="/MyReactApp"/> (as per the answer by #mehamasum)
3) And finally, when doing page refresh on a non-existent SERVER route, you need to redirect what would otherwise be a 404, to the index.html, and let the client-side react-router library do its job. How? In IIS Manager, go to the IIS section\Error Pages\double-click\Edit 404 Status code, and in the 'Edit Custom Error Page' dialog, choose 'Execute a URL on this site', and enter the absolute path /MyReactApp/index.html

Base tag breaks script links in Angular HTML5 mode

Problem
After setting a base tag to run Angular 1.x in HTML5 mode, the browser (Chrome) makes requests to the wrong path for scripts when accessing the app by navigating directly to localhost:3000/resource/id.
Details
I have an Angular 1 app that I have recently set to run in HTML5 mode, like so:
$locationProvider.html5Mode(true);
I have included a base tag in the head of index.html like so:
<base href="/">
I have set up my express routes so that a request for (for example) http://localhost:3000/album/38ad87f will return index.html, like so:
.use('/', express.static(__dirname + '/crate-frontend/app'))
// Playlist endpoints
.use('/api/playlists', playlistRoutes)
// Album endpoints
.use('/api/album', albumRoutes)
// Artist endpoints
.use('/api/artist', artistRoutes)
// Track endpoints
.use('/api/tracks', trackRoutes)
// User endpoints
.use('/api/user', userRoutes)
// Discogs proxy endpoints
.use('/api/discogs', discogsRoutes)
// Youtube proxy endpoints
.use('/api/youtube', youtubeRoutes)
// Search endpoints
.use('/api/search', searchRoutes)
// For serving up crate-frontend/app/index.html
.all('/*', function(request, response){
response.sendFile(__dirname + '/crate-frontend/app/index.html' );
})
Now, when I go to the home page, and navigate around, everything works fine. BUT if I refresh the page that is not the root OR a copy/paste a URL like http://localhost:3000/album/38ad87f into a new window, it breaks. I can see that it receives index.html, but then when the browser requests each of the linked scripts, for example:
<script src="app.js"></script>
it actually makes a request to http://localhost:3000/album/app.js, which returns index.html.
Can someone tell me what I've missed / am doing wrong?
Try to include your ressources with an absolute path and you will be fine for all time.
<script src="/app.js"></script>

Resources