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

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
}

Related

Change all api calls URL react when in production

I would like to know if it is possible to alter all the api calls made from my react app to my backend server. I used to have the static files to sit around in my project root directory, so when I made an api call with axios like await axios.get('/api/my-endpoint') it all worked fine and my requests were translated to GET https://www.example-url.com/api/my-endpoint. But now I am migrating to AWS EC2 and decided to serve static files from AWS S3. So now since the api is in a remote server all my api calls are broken. I need to replace all api calls to https://www.another-example-url.com/api/.... I know I can set the new URL in an environment variable, but that would force me to manually alter all of the api URLs in my front-end codebase. I would not be very happy to do this, since there are almost a thousand of them.
I tried to set the axios baseURL like so:
import axios from "axios";
const instance = axios.create({
baseURL: "www.another-example-url.com"
});
export default instance;
but then all the requests were translated to GET https://www.example-url.com/www.another-example-url.com/api/my-endpoint
Is there a lazy way of doing this? Or do I have to manually alter them all?
You can easily set this using "proxy" field in package.json file of client side. Just add following line
...
"proxy": "https://www.another-example-url.com",
...
edit: I almost forgot that you were asking for production build. So react-amazing-proxy is an alternate to create-react-app proxy which can have feature to use proxy in production build.
Another lazy way, if you are familiar with docker, would be to Dockerize your development built.

How to deploy Next.js app without Node.js server?

I was hoping to deploy a Next.js app with Laravel API. I had developed React apps with CRA and in those I used the API server to serve the index.html of the CRA as the entry point of the app.
But in Next.js, after development I get to know that it needs a Node.js server to serve (which is my bad, didn't notice that). There is an option next export that builds a static representation of the Next.js app and it has an index.html. I am serving the index.html as the entry of the app by my Laravel API. It is serving the page, but just some of the static contents.
What I was hoping to know is it possible to host the aPI and the Next app from a single PHP shared hosting without any node server? If so, how? If not so, what could be the alternatives?
Actually the acepted answer is completly wrong, when you do yarn build and in your package.json is set like "build": "next build && next export", you will get an out folder which all the items in there are used to build without node.js server
Now since you are using laravel, and you use the out folder you will only load half of the page because the routes are not set properly. for that to work you need to edit your next.config.js edit it to
module.exports = {
distDir: '/resources/views',
assetPrefix: '/resources/views',
}
These will set the root directory to the root one in Laravel. now this will work for SPA (single page application) only for the dynamic routes you need to match with a view file for each one that you have in your out folder
For each route that you have you need to create a new "get" route in laravel
Route::get('/', function () {
return require resource_path('views/index.html');
});
Route::get('/contacts', function () {
return require resource_path('views/contacts.html');
});
Route::get('/post/{slug}', function () {
return require resource_path('views/post/[slug].html');
});
Notice that you can pass a wildcard for dynamic routes and they are all gonna work. once you do that and you deploy route out folder inside /resources/views in Laravel it's going to work
Apparently there is no alternative to nodejs server, which is not an option for me currently, so I unfortunately had to abandon next.js and create a CRA app and used as much from the next.js as I could.

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

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.

Deploying React + API apps on two different hosts

I'm trying to figure out what are the best practices to deploy a React app that consumes an API on a different host in production.
Currently in DEV I have the following:
frontend - React app running on webpack server: http://localhost:3000/
backend - API (django-rest) running on: http://localhost:7000/
Right now I define the API url in package.json proxy attribute: "proxy": "http://localhost:7000/", and I make API calls using Axios to api/something/something/ etc.
Both apps are standalone with separate repos and I'd like to keep it that way (I don't want to merge both apps into a single codebase.)
My question:
What is the best way to configure React in production to consume the production URL?
Use process.env.NODE_ENV to find out the current environment and assign the proper url to your base url constant.
In development, you will get process.env.NODE_ENV as "development" and in production, you will get process.env.NODE_ENV as "production".

Build React app with express backend for domain http://example.com

I have a web application in React that I needed to implement a contact form. The application is created using create-react-app and the server folder added. For the form I used sendgrid mail. Does the server work on port 4567, how do the app build to work on the domain? It is a one-page application.
Thx, it is important.
When running in production, a React app is simple HTML, CSS, and JavaScript. These files are sent from your server to a client when requested in the same way that requests/responses are handled for any web page. There are a few steps that need to be done before your React app is ready for production
1: Create a Production Build
First you need to create a production build of your app. This process takes all of your separate .js or .jsx files and puts them together into a single minified file, and the same for .css. Then your index.html is updated to include a link to the CSS and script to the JS. This is done so that only three files will need to be sent rather than the 10s or 100s that exist in development.
If you used create-react-app to start your application, you can use the command:
npm run build
to do this. Otherwise, you need to have webpack installed, and then run:
node_modules/.bin/webpack --config webpack.prod.js --mode production
(which you might want to add as a script to package.json).
See React: Optimizing Performance for more.
2. Serve your Application
Now your server should have a route for your application and when it receives a request on that route, the server should respond by sending index.html from your client/build/ directory (where client/ is the directory of the React app).
Here is an example with Node/Express as the server (in app.js):
const path = require('path');
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname), 'client', 'build', 'index.html');
});
Note that this is just the way to send a static file using Node and can easily be done with any server.
Additional
You mentioned you want to submit forms with your application. If your routes for receiving POST requests match the routes that the forms are on (e.g. form is on /form and server listens for POST on /form) you can just use the default HTML form submission. However this is not a great way to do things when using React because then routing will be controlled by your server rather than by React. Instead you should use some sort of AJAX method to submit the form.
Since your server is now serving your React app (rather than React serving itself as in development), you can just make relative requests and those requests will be made to your server. For example the request (using the fetch API):
const models = await fetch('/api/models');
Will be made to your_host/api/models by default.
in the package.json add
"proxy": "http://localhost:4567"

Resources