Proxy all requests to server in React using createProxyMiddleware - node.js

So on my dev env, I have a React Server on localhost:3000 and a NodeJs server on localhost:5000.
I have used createProxyMiddleware in React to configure the requests. Here is my config:
const {createProxyMiddleware} = require('http-proxy-middleware');
module.exports = function (app) {
app.use(createProxyMiddleware('/auth/google', {target: 'http://localhost:5000'}));
app.use(createProxyMiddleware('/auth/currentUser', {target: 'http://localhost:5000'}));
app.use(createProxyMiddleware('/api/*', {target: 'http://localhost:5000'}));
}
All of these routes work without any problems. For example I have a route like localhost:3000/api/list which gets routed to localhost:5000/api/list.
But now I have a route like localhost:3000/api/list/3132133ffdvf. The random string is some id. This above route does not get proxied. It gives me a 404.
Why is this happening? It shouldn't happen since I have created a proxy for /api/*. Can someone help me out?

The wildcard (*) only matches one directory level. This means that it matches /api/list but not /api/list/id, since the latter has two levels.
Either remove the wildcard, or use a globstar (**):
app.use(createProxyMiddleware('/api', {target: 'http://localhost:5000'}));
or
app.use(createProxyMiddleware('/api/**', {target: 'http://localhost:5000'}));

Related

My CRUD app works locally but not on Heroku

I've created a CRUD app and it works locally, but I can't get it to work fine on heroku. It deploys correctly, the website seems to work, but then I can't get the items I need from the database, as it keeps saying connection refused.
I added the .env variables to Heroku, as well as setting the port to process.env.PORT || 5000 and app.listen(port), I'm not sure what's causing the error. I also have a Procfile with web: node server.js, and a "start" script in package.json that points to server.js. It seems that the server doesn't start at all.
Here the repo in case you want to have a look https://github.com/ThomYorke7/inventory, here the app on heroku https://boardgamenerd.herokuapp.com/
The problem lies in the fact that your application has a backend (server) and a frontend (client) which are served differently locally than on Heroku.
I suppose locally your client is running on localhost:3000 (as it is the default with create-react-app you bootstrapped).
While your backend is running on localhost:5000, your client's package.json contains this line to make it work locally:
"proxy": "http://localhost:5000",
If I visit this page of your app: https://boardgamenerd.herokuapp.com/ > boardgames,
then I face these errors on the browser console:
boardgames-list.jsx:18
Error: Network Error
at e.exports (createError.js:16)
at XMLHttpRequest.p.onerror (xhr.js:83)
xhr.js:178
GET http://localhost:5000/boardgames/ net::ERR_CONNECTION_REFUSED
It tells you that your production version still calls backend on localhost:5000.
I.) First I'd try to fix these fetches by changing to relative URLs.
E.g. the above example (boardgames-list.jsx:18)
❌ your current script has hardcoded localhost fetch at the moment:
useEffect(() => {
axios
.get('http://localhost:5000/boardgames/')
.then((response) => {
setBoardgames(response.data);
setLoading(false);
})
.catch((err) => console.log(err));
}, []);
✔️ make it relative to root by removing "http://localhost:5000":
useEffect(() => {
axios
.get('/boardgames/')
.then((response) => {
setBoardgames(response.data);
setLoading(false);
})
.catch((err) => console.log(err));
}, []);
And it will work on Heroku. In case it wouldn't: see my suggestion below.
II.) Second, a suggestion:
Now your https://boardgamenerd.herokuapp.com/boardgames route uses the following backend endpoint to fetch data: https://boardgamenerd.herokuapp.com/boardgames/
The difference is only the last slash ("/") character which can be confusing and cause more issues later!
It is a best practice to add a differentiator path element to your backend endpoints, like /api/. For example: https://boardgamenerd.herokuapp.com/api/boardgames So you can be sure by first sight which GET request related to the backend and which one to the client.
If you'd go with this solution, you will need to add the following to your server.js:
app.use(express.static(path.join(__dirname, 'client', 'build')))
// required to serve SPA on heroku production without routing problems; it will skip only 'api' calls
if (process.env.NODE_ENV === 'production') {
app.get(/^((?!(api)).)*$/, (req, res) => {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'))
})
}
/^((?!(api)).)*$/ regex skips URLs containing "api" in their path, so they won't be served static as the client/build folder's content - api calls won't be served from static and will work fine.

How can I get create-react-app to use an IP to connect to my api server?

I'm using Facebook's create-react-app. When I start the web-client I see in console:
You can now view web-client in the browser.
Local: http://localhost:3000/
On Your Network: http://192.168.1.107:3000/
The problem is my web-client uses localhost to connect to the api-server, which means I can't use the IP address on different devices to debug issues.
env-variables.js:
export const ENV = process.env.NODE_ENV || 'development';
const ALL_ENV_VARS = {
development: {
GRAPHQL_ENDPOINT_URI: 'http://localhost:4000/graphql',
},
....
I tried updating the above with:
GRAPHQL_ENDPOINT_URI: `http://${process.env.ip}:4000/graphql`,
That did not work, process.env.ip is returning undefined. How can I get the above GRAPHQL_ENDPOINT_URI to use the IP address which somehow create-react-app is getting?
Try adding the following to your client-side package.json:
"proxy": "http://localhost:4000/",
You can then leave the
http://localhost:4000
off of any URLs pointing to the API server from the client side. You would just refer to those addresses as
/graphql/<any additional URL data>
I've performed the same with a Node/Express backend and a React frontend - I resolved the /api portion in my server.js with the following:
//Use our router configuration when we call /api
app.use('/api', router);
just replace /api with /graphql there.
Take a look at this article for further explanation. Hope this helps!
https://medium.freecodecamp.org/how-to-make-create-react-app-work-with-a-node-backend-api-7c5c48acb1b0

Why specify the path in app.use?

//app.js
var app = require('express')();
app.use('/test', require('./test'));
//test/index.js
var router = require('express').Router();
router.get('/test', function (req, res) {
res.status(200).send("success");
});
module.exports = routes;
Why does the path need to be specified in app.use and router.get? If I simply put app.use('/', require('./test')); instead, it seems to work just fine still.
If you change this:
app.use('/test', require('./test'));
to this:
app.use('/', require('./test'));
then you will have the same functionality as before of using the middleware exported by the ./test module on the routes that start with /test so you will experience that everything will work the same, but that middleware will also handle every other route not necessarily starting with /test which, depending on what it does and how it works, may or may not be what you want.
By using some path in the app.use() call you restrict the middleware that you're loading to that path only. When you use / then it's like saying "every path" because every path starts with the slash - even paths that are requested for URLs that doesn't include the slash are still requested with slash, using e.g. with HTTP/1.1 something like:
GET / HTTP/1.1
Host: localhost
With specifying router.get('/test', function (req, res) your handler method will handle any request thats ends in /test. Depends on where the router is use().
app.use(withPath, [callback...]
That mount your middleware functions tests at the speficied path /test
So your middlewares test will be execute when the base request url path match.

How to use local static files while setting up Proxy server

I am setting up proxy server using NODE and EXPRESS. I have the same setup/codebase in local and proxy server. But i want to use all the js,css,images and other static content from local machine and json response from proxy server. Now since the proxy server also has same ,js,css,images, it is picking up everything from proxy. Do I need to restrict urls calls to not pick js,css,images and other static content from proxy but from local. How do i do that? Here is the code structure (both in local and proxy)
/src
/javacode
/WebContent
/js
/css
/images
/jsp
I want everything under /WebContent to be used from local.
This is how i setup proxy:
var proxy = httpProxy.createProxyServer();
app.route('/app/*$').all(function (req, res) { // proxy all requests
proxy.web(req, res, {target: 'http://proxy-server:7001'}); //sandbox
});
Given your file structure, you can use express.static to map your static /WebContent dir to a WebContent virtual path, like this:
var proxy = httpProxy.createProxyServer();
app.use('/app/js', express.static('WebContent/js'));
app.use('/app/css', express.static('WebContent/css'));
app.use('/app/etc', express.static('WebContent/etc'));
app.route('/app/*$').all(function (req, res) { // proxy all requests
proxy.web(req, res, {target: 'http://proxy-server:7001'}); //sandbox
});

How to setup gulp browser-sync for a node / react project that uses dynamic url routing

I am trying to add BrowserSync to my react.js node project. My problem is that my project manages the url routing, listening port and mongoose connection through the server.js file so obviously when I run a browser-sync task and check the localhost url http://localhost:3000 I get a Cannot GET /.
Is there a way to force browser-sync to use my server.js file? Should I be using a secondary nodemon server or something (and if i do how can the cross-browser syncing work)? I am really lost and all the examples I have seen add more confusion. Help!!
gulp.task('browser-sync', function() {
browserSync({
server: {
baseDir: "./"
},
files: [
'static/**/*.*',
'!static/js/bundle.js'
],
});
});
We had a similar issue that we were able to fix by using proxy-middleware(https://www.npmjs.com/package/proxy-middleware). BrowserSync lets you add middleware so you can process each request. Here is a trimmed down example of what we were doing:
var proxy = require('proxy-middleware');
var url = require('url');
// the base url where to forward the requests
var proxyOptions = url.parse('https://appserver:8080/api');
// Which route browserSync should forward to the gateway
proxyOptions.route = '/api'
// so an ajax request to browserSync http://localhost:3000/api/users would be
// sent via proxy to http://appserver:8080/api/users while letting any requests
// that don't have /api at the beginning of the path fall back to the default behavior.
browserSync({
// other browserSync options
// ....
server: {
middleware: [
// proxy /api requests to api gateway
proxy(proxyOptions)
]
}
});
The cool thing about this is that you can change where the proxy is pointed, so you can test against different environments. One thing to note is that all of our routes start with /api which makes this approach a lot easier. It would be a little more tricky to pick and choose which routes to proxy but hopefully the example above will give you a good starting point.
The other option would be to use CORS, but if you aren't dealing with that in production it may not be worth messing with for your dev environment.

Resources