How to forward/proxy WebSocket with NextJS? - javascript

My app is split into two parts. The NextJS front end and a separate Spring Boot GraphQL API backend.
Let's say the NextJS server is https://a.com/ and the Spring Boot server is https://b.com/
How would go about forwarding all requests for https://a.com/graphql to https://b.com/graphql INCLUDING WebSocket stuff, since GraphQL uses WebSockets for it's subscriptions functionality.
I know that you can set up rewrites in next.config.js like this
module.exports = {
async rewrites() {
return [
{
source: '/graphql',
destination: 'https://b.com/graphql',
},
]
},
}
But I have no idea if this method also supports WebSockets or not.

Related

Redirecting backend calls in Vue application

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()

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.

How to make an API call from one local ionic server to another express local server

So I have two applications running locally;
one is ionic2 app running on http://localhost:8041 and another one is express app running on http://localhost:8000.
Now using angular 2 observables when I am making api call with absolute path.
For e.g. From ionic app I making API call to express
getComments(): Observable<any>{
return this.http.get('http://localhost:8000/comment')
......
}
This is resulting into calling an API at http://localhost:8041/localhost:8000/comment since it is running on local host 8041 port.
How do I handle this problem using angular 2? Also is there a way I can do a proxy calling so that making API call from ionic something like '/comment' will call localhost:8000/comment?
edit ionic.config.json file. add your route to api, then restart server ionic serve
"proxies": [
{
"path": "/comment",
"proxyUrl": "http://localhost:8000/comment"
}
]

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