Redirecting backend calls in Vue application - javascript

I am trying to deploy my website written in Vue to a static webhosting service (AWS S3).
I have been using the bencodezen boilerplate which seems to be generated using Vue-CLI. The boilerplate comes with a pre-configured development server such that you can redirect the backend calls (using axios) to either a mock backend or to a production backend:
vue.config.js
module.exports = {
[...]
// Configure Webpack's dev server.
// https://cli.vuejs.org/guide/cli-service.html
devServer: {
...(process.env.API_BASE_URL
? // Proxy API endpoints to the production base URL.
{ proxy: { '/api': { target: process.env.API_BASE_URL } } }
: // Proxy API endpoints a local mock API.
{ before: require('./tests/mock-api') }),
},
}
All calls to myurl.com/api/* will be directed to either the mocked backend or the API_BASE_URL if such is defined. This works fine while using the vue-cli-service serve development server but I'm failing to understand how to translate this to AWS S3, or any other webserver suited for production. If I try to make a login, post-request I get the following response from AWS:
405 Method Not Allowed
Indicating that the call was never redirected (AWS S3 doesn't allow POST calls).
Is it possible to redirect the API-calls directly from the web browser to the backend without going through the webserver? If not, how to I setup my production webserver in such a ways that the calls to myurl.com/api/* are automatically forwarded?

In the production environment (where devServer config does not apply) you need to manually handle the URLs, using some logic to figure out where the requests need to go. If the backend is not on the same host as the frontend, something like this:
const apiPath = process.env.NODE_ENV === 'development' ? '/api' : 'https://mybackendurl.com/api';
axios({
url: `${apiPath}/login`,
method: 'post',
headers: myHeaders,
// etc
})
You probably won't want to do that before every API call so I recommend making some sort of API service you can import and plug into axios. Then you can do something simple like url: API.Login()

Related

HTTP requests in React app are not working over a network connection, but they are working on localhost

I’ve been building a React app for a while now and have been testing responsiveness across multiple devices.
The React app itself works perfectly fine on my local machine. When accessing the React instance over the network, all HTTP requests fail because it wants to send HTTP requests to port 3000 instead of port 5000 which is what my Node.js server is running on.
[1] Compiled successfully!
[1]
[1] You can now view client in the browser.
[1]
[1] Local: http://localhost:3000
[1] On Your Network: http://192.168.1.122:3000
[0] [nodemon] starting `node server.js`
[1] Compiled successfully!
[1] webpack compiled successfully
[0] Server is running on port 5000
[0] MongoDB Connected!
Example of a request in the React app
// Submit application to database
const storeAndSubmit = (formData) => {
try {
// eslint-disable-next-line
const res = axios({
method: 'post',
headers: {
'Content-Type': 'application/json',
},
url: 'http://localhost:5000/api/applications',
data: formData,
});
dispatch({
type: APPLICATION_SUCCESS,
payload: formData.pageNumber,
});
} catch (err) {
dispatch({
type: APPLICATION_FAIL,
});
}
};
Because I don’t know what IP address React will choose when running my start script, I can’t just hard code the IP address into the request. Is there a React environment variable that can be accessed that has the current local network IP address after the start script has been run? If so, I can use that in my HTTP requests and I think that might work.
Error example over the network
xhr.js:210 POST http://192.168.1.122:3000/api/applications 404 (Not Found)
One thing you can do is proxy your requests by adding the following to your package.json file: "proxy": "http://localhost:5000",
Now in your fetch or Axios calls you can use the following URL '/api/applications' instead of 'http://localhost:5000/api/applications'
You are having a networking issue, so let’s go over it in detail.
You have two processes running on your development machine:
a Node.js HTTP server serving the HTML file which loads the React app on localhost:3000. Let’s call this AppServerProcess
a Node.js HTTP server running a controller/driver for the database on localhost:5000. Let’s call this DbServerProcess
So what happens when you are requesting the web application from another device in the network?
The device will make an HTTP request to http://192.168.1.122:3000, where the AppServerProcess will handle the request and respond with the HTML content and the relevant scripts and assets, which will be loaded by the browser. The code in the JavaScript scripts (the web application), will have the fetch code with a URI of http://localhost:5000, which the device will resolve into itself, where it will fail to find anything.
Now, the computer running both processes (DbServerProcess and AppServerProcess) has at least one IP address on the local network, which is 192.168.1.122. This means that if the DbServerProcess is running on localhost:5000, it should also be available on 192.168.1.122:5000, so the URI that should be hardcoded on fetch is http://192.168.1.122:5000/api/applications.
Note that this will also work when working locally, as the IP address will resolve to itself.
Also note that if the computer running both processes has DHCP configured, this IP address may change subject to that configuration, where you seem to have a misconception of what is happening, because it’s not React that chooses that, and it’s not even the AppServerProcess; it’s the OS which has at least one network interface that has a local IP address assigned by the DHCP server running on the router. If you want this to be static, then that is an OS configuration (pretty straight forward on both Windows, macOS and Linux).
Look for "setting static IP address on {operating_system_name}".
I will assume if you specify the React port while starting your app, you will be able to solve this issue and correct me if I am wrong.
You can just do this on Linux:
PORT=3006 react-scripts start
Or this on Windows:
set PORT=3006 && react-scripts start
Check this answer.
Look into cors-npm for your backend server because that maybe the reason for not connecting.
Later you can maybe use Cloudflare Tunnel for your web server and use that address to access your web server in the react app. The link can be provided as an environment variable for the react. see setup react env variable
Firstly, test if the backend API is working or not via Postman.
http://192.168.1.122:5000/ should work on your case.
=========================================================
After it
Try this code on your frontend after checking the backend is working correctly with Postman.
const api = axios.create({
baseURL: "http://192.168.1.122:5000/api/", //PLEASE CONFIRM IP.
headers: {
'Content-Type': 'application/json',
},
});
const submitApi = async (formData) => api.post('/applications', formData);
const storeAndSubmit = async (formData) => {
try {
const {data} = await submitApi(formData);
if(!!data) {
dispatch({
type: APPLICATION_SUCCESS,
payload: formData.pageNumber,
});
} else {
dispatch({
type: APPLICATION_FAIL,
});
}
} catch(e) {
dispatch({
type: APPLICATION_FAIL,
});
}
}
I think you should check the response on the browser in the device you want to connect to the app from.
With the 404 code, the potential reason may be that the device and your computer are not using the same Wi-Fi modem.
You should not use a domain at all:
url: '/api/applications',
or
url: 'api/applications',
The former dictates api to be served from the domain's root, and the latter requires api to be served from the current page's path. In both cases, schema, domain, and port will be inherited from the current page's URL.
Details are in RFC 2396.
It allows you use your code without changes on any domain, any port, as the hosting architecture is not of the frontend app's concern.
Make a .env file and maybe try this:
REACT_APP_API_ENDPOINT=http://192.168.1.blablabla
You can also do:
"scripts" : {
"start": "REACT_APP_API_ENDPOINT=http://localhost.whatever npm/yarn start"
"start-production": "REACT_APP_API_ENDPOINT=http://production.whatever npm/yarn start"
}
It was taken from How can I pass a custom server hostname using React?.

proxy not working for create-react-app in production

I am working with reactjs(create-react-app) to create a dashboard application, In my application i am calling multiple host (for that I have configured multiple proxies in package.json to avoid CORS).
ex- www.app.demo1.com, www.app.demo2.com, www.app.demo3.com...
"proxy": {
"/demo1/api/":{
"target":"www.app.demo1.com"
},
"/demo2/api/":{
"target":"www.app.demo2.com"
},
"/demo3/api/":{
"target":"www.app.demo3.com"
}
}
in application i am calling like-
try{
const host1 = process.env.NODE_ENV === 'production'?
'www.app.demo1.com/demo1/api': '/demo1/api/';
const host2 = process.env.NODE_ENV === 'production'?
'www.app.demo2.com/demo2/api': '/demo2/api/';
const host3 = process.env.NODE_ENV === 'production'?
'www.app.demo3.com/demo3/api': '/demo3/api/';
const resp1 = axios.get(host1)
const resp2 = axios.get(host2)
const resp3 = axios.get(host3)
}catch(){}
in development: when making request to /demo1/api/ it is being proxied to
www.app.demo1.com/demo1/api and i am getting the response. but
in production: I have deployed the application on github pages, although I am getting the below error,
enter image description here
Can anybody help..
Proxies are for development purposes only, and they are handled by webpack-dev-server. On production you need to make the calls to the actual host.
This is created because usually, on development, react is served by a standalone server meant just for that (hence, webpack-dev-server). On production, usually there is a backend (node? ruby? php?) that serves the pages and every call made is going to be to some endpoint with the same hostname.
Example:
In your development environment you have a node server running on port 3001 and your react code running on port 3000. When react fetches /api/user, you actually want http://localhost:3001/api/user, which points to your node server.
In your production environment, you have a server (nginx, maybe?) that forwards all /api calls to your node process, and for everything else it serves your react main index.html file (so you can use react-router, for example). In this case, whenever you request /api/user, this is going to be handled by your web server and routed properly.

fetch request at express root router from react not working

I am trying to call the express root router ('/') from fetch api in react in production mode but it doesn't work.
I am using common server as I am using the react views as static files and renderning like this:
if(process.env.NODE_ENV == 'production'){
app.use(express.static(`${__dirname}/client/build`));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname+'/client/build/index.html'));
});
}
Also, i have already tried using the production hostname as the route in fetch but that doesn't work for me as I am using a proxy.
The complete story is that I am trying to get some tokens that I get from openshift oauth-proxy authentication. There are two containers, one for proxy and another for my web app. When the user is authenticated, proxy sends a token to 'localhost:8080' (which is my web app).
I am handling it this way,
app.get('/', function (req, res) {
if (req.headers !== undefined && req.headers['x-forwarded-access-token'] !== undefined && req.headers['x-forwarded-user'] != undefined) {
userDetails.accessToken = req.headers['x-forwarded-access-token'];
userDetails.username = req.headers['x-forwarded-user'];
req.session.token = userDetails.accessToken;
req.session.username = userDetails.username;
res.send({ accessToken: userDetails.accessToken, username: userDetails.username});
}
});
But when I call the above router from fetch api from react, i don't get the response. The app crashes.
When I am running it on development environment, I am running both on different servers, and when i write it completely, like 'fetch('http://localhost:3001')'
then it works.
I want it to work in production as well. Any help?
More details
The overall flow is, that I have deployed my react app on openshift. I am using oauth-proxy to authenticate the user and then land onto my react app once proxy sends me the authentication token in the react app.
There are two containers, proxy and web-app, inside the proxy-app pod. When i go to the route of the pod, it takes me to the keycloak login page where i enter my credentials, once authenticated, proxy send the auth-token in the headers to it's sidecar container which is my web-app (running on localhost:8080 inside the pod.) Proxy sends the headers to the localhost:8080/ (webapp) which is the root of the web app. Now here, i have to make a fetch request from client to this route and get the token(which i am not able to since it doesn't work this way probably because i am using a proxy which is running on different host than my web app). But when i make request on other routes like ('/api/xyz'), it works like charm. There is something with the root route and proxy that is creating the issue.
It worked when I added cors in my react code. I had it in my server code but not on react.
fetch('/auth', {
credentials: 'include',
mode: 'cors'
})

Dealing with CORS in a create react app [duplicate]

I am trying to make an API call through Axios in my React Application. However, I am getting this CORS issue on my browser. I am wondering if i can resolve this issue from a client side as i dont have any access to the API internally. Attached is my code.
const response = axios({
method: "post",
dataType: "jsonp",
url: "https://awww.api.com",
data: {
appToken: "",
request: {
applicationName: "ddfdf",
userName: "jaime#dfd.com",
password: "dfd",
seasonIds: [1521ddfdfd5da02],
},
},
});
return {
type: SHARE_REVIEW,
payload: "response",
};
Attached is my WebPack.config.js
module.exports = {
entry: ["./src/index.js"],
output: {
path: __dirname,
publicPath: "/",
filename: "bundle.js",
},
module: {
loaders: [
{
exclude: /node_modules/,
loader: "babel",
query: {
presets: ["react", "es2015", "stage-1"],
},
},
{ test: /\.json$/, loader: "json-loader" },
],
},
resolve: {
extensions: ["", ".js", ".jsx"],
},
devServer: {
historyApiFallback: true,
contentBase: "./",
},
node: {
dns: "mock",
net: "mock",
},
};
the simplest way what I found from a tutorial of "TraversyMedia" is that
just use https://cors-anywhere.herokuapp.com in 'axios' or 'fetch' api
https://cors-anywhere.herokuapp.com/{type_your_url_here}
e.g.
axios.get(`https://cors-anywhere.herokuapp.com/https://www.api.com/`)
and in your case edit url as
url: 'https://cors-anywhere.herokuapp.com/https://www.api.com',
The ideal way would be to add CORS support to your server.
You could also try using a separate jsonp module. As far as I know axios does not support jsonp. So I am not sure if the method you are using would qualify as a valid jsonp request.
There is another hackish work around for the CORS problem. You will have to deploy your code with an nginx server serving as a proxy for both your server and your client.
The thing that will do the trick us the proxy_pass directive. Configure your nginx server in such a way that the location block handling your particular request will proxy_pass or redirect your request to your actual server.
CORS problems usually occur because of change in the website domain.
When you have a singly proxy serving as the face of you client and you server, the browser is fooled into thinking that the server and client reside in the same domain. Ergo no CORS.
Consider this example.
Your server is my-server.com and your client is my-client.com
Configure nginx as follows:
// nginx.conf
upstream server {
server my-server.com;
}
upstream client {
server my-client.com;
}
server {
listen 80;
server_name my-website.com;
access_log /path/to/access/log/access.log;
error_log /path/to/error/log/error.log;
location / {
proxy_pass http://client;
}
location ~ /server/(?<section>.*) {
rewrite ^/server/(.*)$ /$1 break;
proxy_pass http://server;
}
}
Here my-website.com will be the resultant name of the website where the code will be accessible (name of the proxy website).
Once nginx is configured this way. You will need to modify the requests such that:
All API calls change from my-server.com/<API-path> to my-website.com/server/<API-path>
In case you are not familiar with nginx I would advise you to go through the documentation.
To explain what is happening in the configuration above in brief:
The upstreams define the actual servers to whom the requests will be redirected
The server block is used to define the actual behaviour of the nginx server.
In case there are multiple server blocks the server_name is used to identify the block which will be used to handle the current request.
The error_log and access_log directives are used to define the locations of the log files (used for debugging)
The location blocks define the handling of different types of requests:
The first location block handles all requests starting with / all these requests are redirected to the client
The second location block handles all requests starting with /server/<API-path>. We will be redirecting all such requests to the server.
Note: /server here is being used to distinguish the client side requests from the server side requests. Since the domain is the same there is no other way of distinguishing requests. Keep in mind there is no such convention that compels you to add /server in all such use cases. It can be changed to any other string eg. /my-server/<API-path>, /abc/<API-path>, etc.
Even though this technique should do the trick, I would highly advise you to add CORS support to the server as this is the ideal way situations like these should be handled.
If you wish to avoid doing all this while developing you could for this chrome extension. It should allow you to perform cross domain requests during development.
Temporary solve this issue by a chrome plugin called CORS. Btw backend server have to send proper header to front end requests.
Another way besides #Nahush's answer, if you are already using Express framework in the project then you can avoid using Nginx for reverse-proxy.
A simpler way is to use express-http-proxy
run npm run build to create the bundle.
var proxy = require('express-http-proxy');
var app = require('express')();
//define the path of build
var staticFilesPath = path.resolve(__dirname, '..', 'build');
app.use(express.static(staticFilesPath));
app.use('/api/api-server', proxy('www.api-server.com'));
Use "/api/api-server" from react code to call the API.
So, that browser will send request to the same host which will be
internally redirecting the request to another server and the browser will feel that It is coming from the same origin ;)
You can have your React development server proxy your requests to that server. Simply send your requests to your local server like this: url: "/"
And add the following line to your package.json file
"proxy": "https://awww.api.com"
Though if you are sending CORS requests to multiple sources, you'll have to manually configure the proxy yourself
This link will help you set that up Create React App Proxying API requests
You can set up a express proxy server using http-proxy-middleware to bypass CORS:
const express = require('express');
const proxy = require('http-proxy-middleware');
const path = require('path');
const port = process.env.PORT || 8080;
const app = express();
app.use(express.static(__dirname));
app.use('/proxy', proxy({
pathRewrite: {
'^/proxy/': '/'
},
target: 'https://server.com',
secure: false
}));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'index.html'));
});
app.listen(port);
console.log('Server started');
From your react app all requests should be sent to /proxy endpoint and they will be redirected to the intended server.
const URL = `/proxy/${PATH}`;
return axios.get(URL);
you must be missing Cors support on your server side code. In Asp.net core you can do it following way.
Add the following call in public void ConfigureServices(IServiceCollection services)
in Startup.cs file.
public void ConfigureServices(IServiceCollection services)
{
app.UseCors(options =>
options.WithOrigins("http://localhost:3000").AllowAnyHeader().AllowAnyMethod());
}
I had the issue and I was in a dev environment (localhost) so my client and server origins where different...
so finally I overcame the problem by writing a simple custom proxy server that runs on localhost, (because I didn't had access to the server code, and I needed to allow cors from the responding origin)
Simple proxy server that runs on localhost:3001:
const express = require("express");
const { createProxyMiddleware } = require("http-proxy-middleware");
var cors = require("cors");
const app = express();
const corsOptions = {
// origin: ["http://localhost:3000"],
origin: true,
credentials: true,
};
app.use(cors(corsOptions));
const proxyMiddleware = createProxyMiddleware("/", {
target: "https://....api origin.....com",
changeOrigin: true,
});
app.use(proxyMiddleware);
app.listen(3001, () => {
console.log("proxy is listening on port 3001");
});
note that my react app is running on port 3000 and my proxy server is on port 3001
because we're using credentials in our request we need to also set origin to our app's origin to our white list, otherwise cors sets "Access-Control-Allow-Origin" to "*" which causes in-browser security error.
react sample login post request through my proxy:
axios.post("http://localhost:3001/Login", dataToPost, { withCredentials: true })
.then((res) => {
if (res.status === 200) {
//all cookies are set in you're browser
console.log(res);
}
})
.catch((err) => {
console.log(err);
});
If you trying to do something like fetch an 3rd party api and you're getting CORS error from the client-side, you can try to do using this extension:
Allow CORS: Access-Control-Allow-Origin
For chrome: https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf
For mozilla: https://addons.mozilla.org/pt-BR/firefox/addon/access-control-allow-origin/
As #Nahush already pointed out, this is actually a server issue and not something that should be handled on the client side. The server is supposed to add the headers for Access-Control-Allow-Origin:
Reference - https://www.w3.org/wiki/CORS_Enabled#How_can_I_participate.3F
Here, I am just adding an easier way to do this on the server side if your server uses express framework. Using the express CORS Middleware is a 2 line code solution for this.
install using -
npm install -S cors
Add the following code to your backend app.
import cors from 'cors';
app.use(cors());
And done.
I did use 2 solutions to dealing with it:
Using http-proxy-middleware. However, please do following below steps on video, instead of read me of library, https://youtu.be/4B5WgTiKIOY . (I did mention the weakness if you use this solution also)
Use Firebase functions to create middleware, not too complicated. I will update detail then here also.
Please let me know if you have any question.

proxy to a running server on localhost in webpack-dev-server

I have a server with an api running at www.mysite.com (for example). My webpack dev server is running at localhost:3000. I want to make it so that all requests are routed as if it were on www.mysite.com.
since I am using webpack#2.2.0 and webpack-dev-server#2.2.0, I noticed that there exists a property called proxy (documentation linked).
Proxying some URLs can be useful when you have a separate API backend development server and you want to send API requests on the same domain.
Is it possible to route all requests to localhost:5000 or some other domain using this API? I've tried something like as follows, but to no avail
proxy: {
'/': {
target: 'www.mysite.com',
pathRewrite: { '^/': "" },
}
}

Categories