React-router URL's, Routing node/express & file structure - javascript

File structure
.root
|-client
| -build
| -node_modules
| -public
| -src
| -components
| -resources
| -data
| -images
| -templates
|-server
| -models
| -public
| -routes
| -server.js
server.js
//Required NPM Packages
const express = require('express'),
app = express(),
cors = require('cors'),
bodyParser = require('body-parser'),
mongoose = require('mongoose'),
methodOverride = require('method-override');
//MongoDB models.
const Product = require('./models/Product');
//Routes.
const indexRoute = require('./routes/index');
const demoRoute = require('./routes/demo');
const bootstrapTemplateRoute = require('./routes/bootstrapTemplate');
//Port.
const _PORT = process.env.PORT || 5000;
//Connect to mongoDB.
mongoose.connect({pathToMongoDB}, {useNewUrlParser:true, useUnifiedTopology:true});
//Setup body-parser.
app.use(bodyParser.urlencoded({extended:true}));
//Allow express/node to accept Cross-origin resource sharing.
app.use(cors());
//Set view engine to EJS.
app.set('view engine', 'ejs');
//Change views to specified directory
app.set('views', path.join(__dirname, '..', 'client','src','Resources','templates'));
app.use(express.static(__dirname+"/public"))
app.use(express.static("client/build"));
app.use(express.static("client/Resources/templates"));
//Setup method override.
app.use(methodOverride("_method"));
//register routes to express.
app.use('/', indexRoute);
app.use('/demo', demoRoute);
app.use('/bootstrapTemplate', bootstrapTemplateRoute)
//listen to established port.
app.listen(_PORT, () => {
console.log(`The server has started on port ${_PORT}!`);
});
module.exports = app;
Question
When I click the back button or load the page in my browser bar nodeJS states that it cannot get the page. I recognise that the front-end and back-end request are different as the React-Router takes care of routing via it's own javaScript allowing for zero page reloads, but how do you actually solve the problem on nodeJS/Express?
Also when I go to localhost:3000/demo it returns the data from my mongoDB and renders it in json format rather then loading the correct page.
Currently working on a MERN Stack with the below Nginx basic routing config.
http{
server{
listen 3000;
root pathToRoot/client/build;
location / {
proxy_pass http://localhost:5000/;
}
}
}
events {
}
I believe the problem is within my routing via express/nodejs. I can't create express specific routes and render the correct page because react is rendering it for me. I looked at the following question React-router urls don't work when refreshing or writing manually. Should I just do a catch-all and re-route back to the main index page? This seems like it would render bookmarks unusable.
Edit
Here are the 2 node routes.
index.js
const express = require('express'),
router = express.Router();
router.get("/", (req,res) => {
//Render index page.
res.render("index");
});
demo.js
const express = require('express'),
router = express.Router();
const Product = require('../models/Product');
router.get('/:searchInput', (req,res) => {
Product.find({ $text: {$search: req.params.searchInput}}, (err,foundItem) => {
if(err){
console.log(err);
}else{
res.send(foundItem);
}
})
})
router.get('/', (req, res) => {
Product.find({}, (err, foundItem) => {
res.send(foundItem);
});
});
module.exports = router;

I would try separate out your development folders from your build folders as it can get a bit messy once you start running react build. A structure I use is:
api build frontend run_build.sh
The api folder contains my development for express server, the frontend contains my development for react and the build is created from the run_build.sh script, it looks something like this.
#!/bin/bash
rm -rf build/
# Build the front end
cd frontend
npm run build
# Copy the API files
cd ..
rsync -av --progress api/ build/ --exclude node_modules
# Copy the front end build code
cp -a frontend/build/. build/client/
# Install dependencies
cd build
npm install
# Start the server
npm start
Now in your build directory you should have subfolder client which contains the built version of your react code without any clutter. To tell express to use certain routes for react, in the express server.js file add the following.
NOTE add your express api routes first before adding the react routes or it will not work.
// API Routing files - Located in the routes directory //
var indexRouter = require('./routes/index')
var usersRouter = require('./routes/users');
var oAuthRouter = require('./routes/oauth');
var loginVerification = require('./routes/login_verification')
// API Routes //
app.use('/',indexRouter);
app.use('/users', usersRouter);
app.use('/oauth',oAuthRouter);
app.use('/login_verification',loginVerification);
// React routes - Located in the client directory //
app.use(express.static(path.join(__dirname, 'client'))); // Serve react files
app.use('/login',express.static(path.join(__dirname, 'client')))
app.use('/welcome',express.static(path.join(__dirname, 'client')))
The react App function in the App.js component file will look like the following defining the routes you have just added told express to use for react.
function App() {
return (
<Router>
<div className="App">
<Switch>
<Route exact path="/login" render={(props)=><Login/>}/>
<Route exact path="/welcome" render={(props)=><Welcome/>}/>
</Switch>
</div>
</Router>
);
}
Under the components folder there are components for login and welcome. Now navigating to http://MyWebsite/login will prompt express to use the react routes, while for example navigating to http://MyWebsite/login_verification will use the express API routes.

Related

Running an Express application inside an Express application

I have two projects. First a single page app (without bundler) which is being started by the following server.js:
const express = require("express");
const morgan = require("morgan");
const path = require("path");
const DEFAULT_PORT = process.env.PORT || 8000;
// initialize express.
const app = express();
// Initialize variables.
let port = DEFAULT_PORT;
// Configure morgan module to log all requests.
app.use(morgan("dev"));
// Setup app folders.
app.use(express.static("app"));
// Set up a route for index.html
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname + "/index.html"));
});
// Start the server.
app.listen(port);
console.log(`Listening on port ${port}...`);
Secondly, I also have a separate Express application which handles authentication and has multiple routes and templates/views. However, I want one of those routes to contain the single page app instead of a simple view. I copied the whole repository of my first project into the Express applications src-directory and would now like to make it available under the route "/app".
Is that possible? How would I make sure that the static files of the single page app are being used properly?
router.js of the Express application
const getRoutes = (mainController, authProvider, router) => {
const authorizationMiddleware = require("./authorizationMiddleware");
// app routes
router.get("/", (req, res, next) => res.redirect("/home"));
router.get("/home", mainController.getHomePage);
router.get("/app", (req, res) => {
???
});
...
project structure:
-src
--first projects dir
--data
--msal-express-wrapper
--public
--utils
--views
--app.js
--authorizatonMiddleware.js
--controller.js
--router.js
-appSettings.js
-package.json
-...

How to deploy Node JS app cPanel from localhost to a server

I have created a simple Express JS app. and it is working fine in localhost. when I visit localhost:8000 I see static files (index.html, style.css and frontend.js).
I have tried to deploy that app in a server using cPanel. and I have installed Node app and dependencies using package.json successfully. But when I visit the domain I just see a message (Node JS app is working, Node version is 10.24.1).
How to make my app to point and display the static folder (index.html) and run the app?
My app architecture:
server.js
package.json
public/index.html
public/style.css
public/frontend.js
And here is my server.js startup file:
// Setup empty JS object to act as endpoint for all routes
projectData = {};
// Require Express to run server and routes
const express = require('express');
// Start up an instance of app
const app = express();
/* Dependencies */
const bodyParser = require('body-parser');
/* Middleware*/
//Here we are configuring express to use body-parser as middle-ware.
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Cors for cross origin allowance
const cors = require('cors');
app.use(cors());
// Initialize the main project folder
app.use(express.static('public'));
// Setup Server
const port = 8000;
const server = app.listen(port, function(){
console.log(`server running on localhost: ${port}`);
});
//POST Route to store data in the app endpoint, projectData object
app.post('/addData', addData);
function addData (req, res){
let data = req.body;
projectData = data;
console.log(projectData);
}
app.get('/getData', getData);
function getData(req, res) {
res.send(projectData);
}
The problem here is that you are not pointing a route to send the HTML file. Otherwise the client would have to point it to the correct path of the file, Like localhost:3000/index.html.
you need to send it from the server using app.get
app.get("/", (req, res) => {
res.sendFile(__dirname + "path to the file");
});
The problem was that I have created the app in a subfolder of my domain.
But when I have created subdomain and reinstalled the app inside it, the app is pointing to static folder successfully.

how to use serve static file with express and react?

I have a project made with create-react-app and i would like to use it within express ( as i need some back end functionnality).
basically, my server render a blank page when i run it....
here is the code of my server:
const express = require('express');
const path = require('path');
const app = express();
// Serve the static files from the React app
app.use(express.static(path.join(__dirname, 'public')));
// An api endpoint that returns a short list of items
app.get('/api/getList', (req,res) => {
var list = ["item1", "item2", "item3"];
res.json(list);
console.log('Sent list of items');
});
// Handles any requests that don't match the ones above
app.get('*', (req,res) =>{
res.sendFile(path.join(__dirname , 'public', 'index.html'));
});
const port = process.env.PORT || 5000;
app.listen(port);
console.log('App is listening on port ' + port);
if i enter the endpoint http://localhost:5000/api/getList, i can see the result, but when i want to display my react app for the other end point, the page is blank.
The react app use different Route and i also added a proxy in the package.json.
my server source file is at the root of the react app like the image.
Does someone has an idea why the react app doesnt show?
Thanks
You have two lines that are kind of doing the same thing:
// Serve the static files from the React app
app.use(express.static(path.join(__dirname, 'public')));
Will serve your index.html file to http://localhost:5000/index.html
According to the Express documentation https://expressjs.com/en/starter/static-files.html
And then it seems like you have the second route:
app.get('*', is formatted incorrectly.
It would need to be app.get('/*'.
If you aren't directing the user to multiple routes then it might be better to just use the root, app.get('/', rather than trying to catch all routes.

Returning a page through HTTP with express

I have a folder called "Public" which contains an index.html file a long with some JavaScript and library files. When someone tries to access the products path (mydomain.com/products) I want to display that index.html file, but the client also needs to receive all the JavaScript and libraries. Here is the code for how I initially handle the HTTP request.
const express = require('express')
const app = express()
const bodyParse = require('body-parser')
const productRoutes = require('./api/routes/products')
const orderRoutes = require('./api/routes/orders')
app.use(bodyParse.urlencoded({extended: false}))
app.use(bodyParse.json())
// Routes which handle requests
app.use('/products', productRoutes)
app.use('/orders', orderRoutes)
In the products.js file, I continue the routing like this:
const express = require('express')
const router = express.Router()
router.get('/', (req, res, next) => {
/*res.status(200).json({
message: 'Handling GET requests to /products'
})*/
res.status(200).render("../../public")
})
The code I've commented out works perfectly fine, but I'm struggling to respond with the "public" folder page. I can't remember everything I've tried, but using .render or .sendFile on the "public" directory has not worked for me.
When I try to access the /products route, I'm hit with an empty error message. As it fails to return anything in the /products route, in continues down the file to an error handler. The error message is empty.
Any ideas on how to display the page with all the folder contents would be great!
Try: app.use('/products', express.static('public'))
This makes your "public" directory viewable from the /products route.
express.static() docs
You must config path for express static by :
//app.js | server.js
app.use(express.static(__dirname + '/public'));
Then, in example you have a file as : /public/you.html
In your app, you can use that file as path /you.html
And the same with all files type *.js, *.css,...
Fix error cannot view error
Run cmd npm install ejs
Att to app.js:
app.set('view engine', 'ejs');
After that, you create 1 file error.ejs at folder views :
//error.ejs
<%=error%>
Goodluck

NodeJS Express defining custom routes

This is probably a really basic concept that I'm not understanding but in my NodeJS application I am trying to define a custom route.
my directory structure is as follows
/application
/app.js
/package.json
/node_modules
/public
/routes
/control
/users.js
/views
/control
/users.ejs
Which I am happy with because I want to keep the routes and views in a 1 to 1 relationship because I will eventually end up with something like
/application
/app.js
/package.json
/node_modules
/public
/routes
/control
/users.js
/system.js
/tools
/stock.js
/report.js
/views
/control
/users.ejs
/system.ejs
/tools
/stock.ejs
/report.ejs
So I don't want to end up with a /routes/index.js file with a hideous amount of routing code inside.
It seems to work while my app.js file is as follows
//==============================================================================
// setup
//==============================================================================
var express = require("express");
var path = require("path");
var app = express();
var port = 3000;
var message = null;
app.set("view engine", "ejs");
app.use(express.static(path.join(__dirname, "public")));
//==============================================================================
// routes
//==============================================================================
var users = require("./routes/control/users");
app.get("/", users.users);
//==============================================================================
// start server
//==============================================================================
app.listen(port, function() {
message = "Server Started : Port " + port;
console.log(message);
});
Although I can see this is going to end up looking like
//==============================================================================
// setup
//==============================================================================
var express = require("express");
var path = require("path");
var app = express();
var port = 3000;
var message = null;
app.set("view engine", "ejs");
app.use(express.static(path.join(__dirname, "public")));
//==============================================================================
// routes
//==============================================================================
// control
var users = require("./routes/control/users");
app.get("/", users.users);
var system = require("./routes/control/system");
app.get("/", system.system);
// tools
var stock = require("./routes/tools/stock");
app.get("/", stock.stock);
var report = require("./routes/tools/report");
app.get("/", report.report);
//==============================================================================
// start server
//==============================================================================
app.listen(port, function() {
message = "Server Started : Port " + port;
console.log(message);
});
So I don't really want to have that many requires but doing it like the following doesn't seem to work and I'm not sure why
// control
var control = require("./routes/control");
app.get("/", control.users.users);
app.get("/", control.system.system);
// tools
var tools = require("./routes/tools");
app.get("/", tools.stock.stock);
app.get("/", tools.report.report);
You can use the express Router object to chain your routes. Here's an example
/app.js
var routes = require('./routes/index');
// as noted by Paul in the comments,
// you could use `app.use(routes)` for simplicity
app.use('/', routes);
/routes/index.js
var routes = require('express').Router();
routes.route('/test')
.get(function (req, res) {
res.send(req.originalUrl);
});
routes.use('/control', require('./control/user'));
module.exports = routes;
/routes/control/user.js
var routes = require('express').Router();
routes.route('/test')
.get(function (req, res) {
res.send(req.originalUrl);
});
module.exports = routes;
So for the route defined in index.js, you'll need to send a GET request to /test while in user.js, you will need to send a GET request to /control/test to get a response.
This way all you need to include in the main js file, app.js in your case, is the main routes file, which is index.js under the routes directory. Either way, you will still need to do one require statement for each file that exports a router object.
When you say:
var control = require("./routes/control");
Node will do the followings:
Since you privided a relative path to require, it will search for a routes directory within the current folder and in that directory search for a control.js file. It can't find control.js file so then:
Search for a control.json file. but it also can't find that
file. then:
Search for a control directory. It finds such a directory. opens
it and search for a package.json file to look into its
main property. but it also cant find such a file. then:
Search for a index.js file but also there is no such a file
By default when you give a folder path to require, it does not load .js files inside that folder automatically it looks for package.json file and if it's not there it loads index.js. i. e. It looks for an entry point.
So make an index.js in your control folder:
/routes
/control
/users.js
/system.js
/index.js
index.js:
module.exports = {
users: require('./users');
system: require('./system');
};
Do this also for your tools directory and your last approach should work.
Note that you could consider using express.Router to manage routes.

Categories