Stop statically served React app from calling server path - node.js

My app has the default homepage at "/", and a contact page at "/contact". When I ran the react server(localhost:3000) and express server(localhost:8000) separately, the navigation between these pages works fine as handled by "react-router-dom" below.
Frontend React, Routing.tsx:
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Contact from "../pages/Contact/Contact";
import Main from "../pages/Main/Main";
import Error from "../pages/Error/Error";
function Routing() {
return (
<BrowserRouter>
<Switch>
<Route path="/error" component={Error} />
<Route path="/contact" component={Contact} />
<Route path="/" component={Main} />
</Switch>
</BrowserRouter>
);
}
export default Routing;
Now I built the react app using npm run build, and placed the "build" folder inside my backend express server to be served, as per deployment doc here. Then I ran "npm run dev" to start the server.
However, whenever I try to navigate to the /contact page, it issues a server call to "localhost:8000/contact" instead of being handled by the frontend routing. Of course the server doesn't have that route, and all my server routes are prefaced with "/api/" anyway.
How can we prevent frontend navigation from calling the server routes?
More code below, thanks.
Backend Express, App.ts:
import express from "express";
import path from "path";
class App {
private _app: express.Application;
private readonly _port: number | string = process.env.PORT || 8000;
constructor(controllers: any[]) {
this._app = express();
this.initializeControllers(controllers);
this.initializeMiddleWares();
this.initHostingReactUI();
}
public start() {
this._app.listen(this._port, () => {
console.log(`App listening on the port ${this._port}`);
});
}
private initializeControllers(controllers: any[]) {
controllers.forEach((controller) => {
this._app.use("/api", controller.router);
});
}
public initializeMiddleWares() {
require("./src/middleware/express.middleware")(this._app);
}
public initHostingReactUI() {
// I am aware that you can do "/*" below to catch all routes, but that doesn't solve my issue as it still calls the backend for every non-api routes that should be handled by frontend routing.
this._app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "build", "index.html"));
});
}
}
export default App;
Backend Folder structure with Build folder:
If needed:
Backend github source.
Frontend github source

There are two ways to combine the routes between the frontend and backend:
If access from http://localhost:3000
You need to boot both frontend and backend.
In this case, all your backend request urls will start with http://localhost:3000 (if you didn't specify a base url). To solve this issue, add a line in the root of your client-side package.json file:
{
"proxy": "http://localhost:8000"
}
After that, frontend will redirect all the unknown routes to backend.
If access from http://localhost:8000
You only need to boot the backend.
Since React application is one-page application, which means there is only one html entry (index.html). Now we need a route to serve the frontend routes in the backend:
// place this in the end after the other routes
app.get('*', (req, res) =>
res.sendFile(path.join(__dirname, "build", "index.html"));
})
The routing system will check if the route exists among the backend routes. If not, then serve index.html and check if the route exists among the frontend routes. Therefore, in the frontend you should guard the urls by returning a 404 page when not found.

Do you mean navigate through the app? if not, navigating to the page by the URL or refreshing the page always will send a request to the server, which should return the app index.js.
All you need to do is to place the last route which is serving react (You can use app.get('*', .....) or placingapp.use((req, res) => {...}) without route).
In this case, when the request came to the server, the server will search for the route top-to-bottom, if the requested route is not api then it's will serve the client-side app.

Related

cannot post ; post can't be made with axis on react

I am trying to make a post request in React to the server
my React app is running at port 3000 & express app is running at port 9000
React:
axios.post("/").then((response)=>{
console.log(response.data)
}).catch((e)=>{
console.log(e.response.data)
this.out = e.response.data
})
Express:
app.post("/", (req, res) => {
console.clear()
console.log(req.body)
res.end("req")
})
on the web console it says :
"Failed to load resource: the server responded with a status of 404 (Not Found)"
on the app 'postman' It runs just fine
I tried to follow this YT tutorial https://www.youtube.com/watch?v=kJA9rDX7azM
First you need to check weather you add a proxy in your React APP project Package.Json file under the private dependency name,
proxy:
'http://localhost:9000/'
Axios first look for the port you are requesting for in the current server which is in you case is / so / is also a port in frontend so it never goes for the backend be safe to go with
axios.post("http://localhost:9000/")
.then((response)=>{
console.log(response.data)
})
.catch((e)=>{
console.log(e.response.data)
})
Secondly make sure you must install the axios dependency in your react project
Seems like you forgot to add domain in the axios request.
axios.post("http://localhost:9000/").then((response)=>{
console.log(response.data)
}).catch((e)=>{
console.log(e.response.data)
this.out = e.response.data
})

express app is not sending index.html file to client

So my express app has a small Node server setup so it can serve up the index.html file when the home route '/' is hit. This is a requirement of using the App Services from Azure, there has to be this server.js file to tell the server how to serve up the client, and i had a previous implementation of this working, however i wanted to change my file structure. previously i had, the client React app in a folder client and the server.js in a folder server along with all of the conrtollers and routes. i've since moved the server API to its own application as there are other apps that depend on it. and i moved the client up one directory into the main directory. Everything was working fine till the other day when all of the sudden when you hit the home route / it will not serve up the index.html file. if you hit any other route it works, if you even hit a button linking back to the homepage, it works, but it wont serve up the app from the / and i cannot for the life of me figure out why, on my development server there are no errors in the console. and im most definitely targeting the correct directory and place for the index. but its like the server isnt reading the route to serve up.
if (process.env.NODE_ENV === 'production') {
console.log('running');
app.use(express.static(path.resolve(path.join(__dirname, 'build'))));
// no matter what route is hit, send the index.html file
app.get('*', (req, res) => {
res.sendFile(path.resolve(path.join(__dirname, 'build', 'index.html')));
});
} else {
app.get('/', (req, res) => {
res.send('API is running...');
});
}
So here im saying if the NODE_ENV is in production make the build folder static, and then whatever route is hit. (Note: i also tried this app.get with other route formats such as /* or / all have the same issues. however in my previous iteration when the client and server where deployed in the same location, /* is what i used.) The .env varialbes are setup correctly, as when the server is ran, itll console log running.. but even if i put a console log inside of the app.get() its like its never hit unless i access the route from something else first.
for example, if i place a console log inside of app.get that states hit whenever the route is hit, hitting / directly does nothing, but if i go to /login itll serve up the correct html on the client and console log hit in the terminal...
If you are having server files inside the client react app, then we are basically accessing file which are not inside our server file. So, we can serve static files using the following code:
const express = require("express");
const app = express(); // create express app
const path = require('path');
app.use(express.static(path.join(__dirname, "..", "build")));
app.use(express.static("build"));
app.listen(5000, () => {
console.log("server started on port 5000");
});
Now in your packages.json of the client react app change the name of start tag under scripts tag to start-client. Then add this following tag to the scripts tag:
"start":"npm run build && (cd server && npm start)",
Basically, this will build the react app and start the server.
It should look like this :
Also in the packages.json of your server add the following tag under script tag
"start":"node server.js"
So when you run the following command npm start it should look like this :

File serve in shopify app dev not working

I've created shopify app with node.js app template
npm init #shopify/app#latest
Folder structure is at the bottom
And run npm run dev
It's ok for api endpoints.
What I wanna do is to serve static files. In fact, this is an express.js server and I created a static folder in web folder
app.use(serveStatic('static'));
But I can't access static files. I tried app.use(serveStatic("${process.cwd()}/static")). The above stuff is working on a normal express.js project. But it does not work with shopify cli and vite config.
Vite config is
const config = {
test: {
globals: true,
exclude: ["./frontend/**", "./node_modules/**"],
},
};
export default config;
I finally got it.
I noticed that:
It works if you were to load using localhost:PORT/path/to/static.file. You can print out PORT in your web/index.js.
This simple middleware doesn't get triggered when requesting your static file through ngrok but it does get triggered by number 1 above.
app.use((req, res, next) => {
console.log("Backend hit!");
next();
});
That means the backend never got the static file request. My understanding is vite receives all the requests then proxies some of them to the backend using a config.
The config Shopify gave is not proxying the static file request so you'll need to modify the proxy config.
vite.config
...
export default defineConfig({
...
server: {
host: "localhost",
port: process.env.FRONTEND_PORT,
hmr: hmrConfig,
proxy: {
"^/(\\?.*)?$": proxyOptions,
"^/api(/|(\\?.*)?$)": proxyOptions,
// The next line is what I added
"^/static/.*$": proxyOptions,
},
},
});
web/index.js
app.use("/static", express.static(`${process.cwd()}/public`));
I'm mounting my static files on "/static" but feel free to modify the proxy line to suit your needs.

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.

Problems running an Angular 9 app in NodeJS

I have a working NodeJS server and an Angular 9 app. I'm able to test the app in development mode, it works perfectly.
But, when I build the app with ng build --prod and try to access it with NodeJS server I get several errors about file load:
Refused to apply style from
'http://localhost:8080/styles.09cf2cc3740ba29d305c.css' because its
MIME type ('text/html') is not a supported stylesheet MIME type, and
strict MIME checking is enabled.
GET http://localhost:8080/runtime.689ba4fd6cadb82c1ac2.js
net::ERR_ABORTED 404 (Not Found)
I have a proxy file in the app to redirect all its petitions to NodeJS:
proxy.conf.json
{
"/api/*": {
"target": "http://localhost:8080",
"secure": false,
"logLevel": "debug",
"changeOrigin": true
}
}
Am I missing something?
proxy.conf.json aims to provide you an easy way to access to backend by rewriting url in development environment, by using ng serve.
For example, by accessing http://localhost:4200/api, in fact, you access http://localhost:3000/api. (so your backend).
But here, you're issue is how to serve Angular files with NodeJS.
Here is a minimal express code which serves an API endpoint /api, and also static files inside public sub-directory.
const express = require('express')
const app = express()
app.use(express.static('public'));
app.get('/api', function (req, res) {
res.send({ message: 'api ok' })
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
Copy all files generated by ng build --prod, (see inside dist folder, and your-app-name subfolder) to public folder.
node app.js
You will be able to access your Angular app at http://localhost:3000, and your Angular app will be able to access your API at http://localhost:3000/api.

Resources