How to configure Express app with Vue Router? - node.js

I'm building an Express based app that works as follows:
'/' static main site served by Express with pug templating engine
'/admin' admin panel handled by VueJS with Vue Router in history mode
At the moment if I go to '/admin/about' it works fine, but when I refresh the page it throws the 404 error. How do I configure the server to handle all admin routes ('/admin/xxx') with Vue Router but all main site routes with Express (as it currently does)?
I've tried using connect-history-api-fallback middleware but with no success.
app.js
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const history = require('connect-history-api-fallback');
const adminRoutes = require('./routes/admin');
const publicRoutes = require('./routes/index');
app.set('view engine', 'pug');
app.set('views', 'views');
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/admin', adminRoutes);
app.use(publicRoutes);
app.use(history());
app.use((req, res, next) => {
res.status(404).render('404', {
pageTitle: 'Page Not Found'
});
});
app.listen(3000);
routes/index.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res, next) => {
res.render('home/index', {
pageTitle: 'Lorem Ipsum',
path: '/'
});
});
module.exports = router;
routes/admin.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res, next) => {
res.render('admin/index', {
pageTitle: 'Admin Panel',
path: '/'
});
});
module.exports = router;

Your current configuration handles just root path / for the admin SPA(so you get 404 on all admin pages except the root if you tries to refresh page). You can allow all admin urls to be "processed" by Vue app on client(even 404 errors) with /* instead of / in your route.
But there will be a problem with http status codes because (as you probably guessed) server will always return 200 for every route... but I think most of developers are ok with this and just showing some 404-page-component for user if there is no component matched the url.
If you want to see correct http codes in your browser for web consistency, debugging, project requirements or something - without SSR, you will have to repeat your client routes in back-end I think.

Related

React/Express application only loads on root route, otherwise it loads as JSON

I have a React/Express Application, and I'm using React Router. When I hit the URL http://myapp.com, the React app will load no problem, and I'll have no problem going to other routes. The issue occurs if I were to instead directly visit any other route, for example http://myapp.com/posts or http://myapp.com/login - there, it will load to the screen the JSON data that the component would have used. If I were to visit myapp.com and then nagivate to myapp.com/posts and hit the refresh, it will load the JSON data instead.
Any idea how I can get every route to send the static files for every route?
Here is what I have.
The structure I have is as follows:
client/
build/
node_modules/
public/
index.html
src/
components/
redux/
.gitignore
index.js
package.json
models/
node_modules/
public/
routes/
.env
.gitignore
package.json
server.js
Server.js:
require('dotenv').config()
const express = require('express')
const cors = require('cors')
const mongoose = require('mongoose')
const postsRouter = require('./routes/posts')
const commentsRouter = require('./routes/comments')
const usersRouter = require('./routes/users')
const path = require('path')
const session = require('express-session')
const passport = require('passport')
var cookieParser = require('cookie-parser')
const LocalStrategy = require('passport-local')
const mongoSanitize = require('express-mongo-sanitize')
const helmet = require('helmet')
const User = require('./models/user')
const PORT = process.env.PORT || 3001
const app = express()
app.set('views', path.join(__dirname, 'views'))
app.use(
helmet({
contentSecurityPolicy: false,
})
)
app.use(express.json())
app.use(
cors({
origin: herokuUrl,
credentials: true,
methods: ['GET', 'PUT', 'POST', 'OPTIONS', 'DELETE'],
})
)
app.use(express.static(path.join(__dirname, 'client', 'build')))
app.use(express.static(path.join(__dirname, 'public')))
app.use(cookieParser(process.env.COOKIE_SECRET))
app.use('/posts', postsRouter)
app.use('/posts/:id/comments', commentsRouter)
app.use('/', usersRouter)
app.all('*', (req, res) => {
res.status(404).json({ message: 'Invalid search term' })
})
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'client', 'build', 'index.html'))
})
app.listen(PORT, () => {
console.log(`listening on port ${PORT}`)
})
Thank you in advance
Thanks for updating the question with more information.
It seems your issue is that you have react routes at /posts, /login, etc. and also API routes at the same locations.
When react-router-dom goes to a new location, it doesn't make another request to your server as it already has all the files it needs (i.e. your index.js, css files, etc.), but when you refresh the page your browser is making a GET request to that location (e.g. /posts).
Your express server is seeing the request and responding with JSON.
What you will need to do is make sure that you don't have GET request handlers with the same path as you have routes.
My recommendation (though there are plenty of other ways) is to put your API handlers at /api. For example, /api/posts. Then, when you go to the url at /posts it will load from the static files, but when you make a GET request to /api/posts express will know to serve the JSON.

Express server middleware execute twice?

After trying to log some data on index file. I found my express server execute twice. Why do i get this error/bug?
Running Node 12.13.0 LTS, Express 4.17.1 and latest packages versions by the date of this post. I’ve tried on commenting some parts of code and always seem to end up running twice.
My app.js code:
const express = require('express');
const expressLayouts = require('express-ejs-layouts');
const path = require('path');
const bodyParser = require('body-parser');
const favicon = require('serve-favicon');
const app = express();
// ENV Variables
require('dotenv').config();
const PORT = process.env.PORT;
// Authentication Packages
const session = require('express-session');
const passport = require('passport');
// Middlewares
app.use(favicon(path.join(__dirname,'public','images','favicon.ico')));
app.use(bodyParser.urlencoded({extended: false}));
app.use(express.json());
app.use(session({
secret: 'GBR6N7^?5Xx-Ldqxf&*-Hv$',
resave: false,
saveUninitialized: false,
//cookie: { secure: true }
}));
app.use(passport.initialize());
app.use(passport.session());
// View Engine
app.use(expressLayouts);
app.use(express.static(path.join(__dirname, 'public')));
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// Routes
app.use('/', require('./routes/index'));
// Controllers
app.use('/profile', require('./routes/profile'));
app.use('/products', require('./routes/products'));
app.use('/bookmarks', require('./routes/bookmarks'));
// Catch 404
app.use((req, res) => {
res.render('pages/404');
});
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
and my index.js code:
const express = require('express');
const router = express.Router();
// Official pages
router.get('/', (req, res) => {
// THIS IS THE CODE I GET TWICE ON CONSOLE
console.log(req.user);
console.log(req.isAuthenticated());
// THIS IS THE CODE I GET TWICE ON CONSOLE
res.render('pages/index');
});
router.get('/about', (req, res) => {
res.render('pages/about');
});
router.get('/features', (req, res) => {
res.render('pages/features');
});
// Footer pages
router.get('/terms', (req, res) => {
res.render('pages/terms');
});
router.get('/refunds', (req, res) => {
res.render('pages/refunds');
});
module.exports = router;
Also i have those two functions on my profile.js (for passport.js):
passport.serializeUser((userId, done) => {
done(null, userId);
});
passport.deserializeUser((userId, done) => {
done(null, userId);
});
I get those results twice:
console.log(req.user);
console.log(req.isAuthenticated());
Output (Executed twice!):
undefined
false
undefined
false
and I expect one:
undefined
false
Due to how Express routing works, the path / will match / and /about and /favicon.ico etc. This is because Express supports not just endpoint routing but also path mounting. In other words, express supports things like this:
const app = express();
const route = express.Router();
route.get('/world', (req, res) => { res.send('hello') });
app.get('/hello', route); // mounts world to hello
// so we can access /hello/world
In order to support the feature above, express needs to interpret paths such as /hello to mean both /hello and /hello/anything/else. It needs to treat it as both the endpoint and potentially just a path leading to an endpoint.
This means that if you have a path:
app.get('/', () => {});
It will also trigger if the browser requests /favicon.ico. And browsers request favicon.ico to draw the tiny icon in the browser tab. This is why your route is triggered twice.
A few things to keep in mind when writing Express routes/controllers:
Make sure that the / path is last because otherwise it will also respond to requests to all your other paths.
If you are using express.static() make sure it is set up before the / path. Yes, the first rule above should also cover this but I see this issue often enough that it merits its own point.
In your case you can possibly fix the issue by simply creating a favicon.ico icon and saving it in the static (public) folder.

All pages return the same content with OVH Cloud Hosting

I've recently completed a Node.js website locally, which works fine, though I have noticed some issues when uploading the website online to OVH Cloud Web Hosting. All pages, even those that should not exist, return the contents of the homepage.
I'm running Express with Node.js, and the file structure was created with express-generator. My app.js file contains the following:
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var expressHbs = require('express-handlebars');
var Handlebars = require('handlebars');
var HandlebarsIntl = require('handlebars-intl');
var mongoose = require('mongoose');
var indexRouter = require('./routes/index');
var blogRouter = require('./routes/blog');
var blogItemRouter = require('./routes/blog-item');
var portfolioRouter = require('./routes/portfolio');
var contactRouter = require('./routes/contact');
var iBlogPostsRouter = require('./routes/i/blog-posts');
var iContactRouter = require('./routes/i/contact');
var iPortfolioItemsRouter = require('./routes/i/portfolio-items');
var portfolioItemRouter = require('./routes/portfolio-item');
HandlebarsIntl.registerWith(Handlebars);
var app = express();
// view engine setup
app.engine('.hbs', expressHbs({defaultLayout: 'layout', extname: '.hbs'}));
app.set('view engine', '.hbs');
app.disable('etag');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/blog', blogRouter);
app.use('/blog/:title', blogItemRouter);
app.use('/portfolio', portfolioRouter);
app.use('/contact', contactRouter);
app.use('/i/blog-posts', iBlogPostsRouter);
app.use('/i/contact', iContactRouter);
app.use('/i/portfolio-items', iPortfolioItemsRouter);
app.use('/portfolio/:title', portfolioItemRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {title: err.status + ' ' + err.message});
});
module.exports = app;
All of the routers have the same code, except render a different template. Here is an example of the indexRouter:
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'About', about: true });
});
module.exports = router;
The issue is that every page, even my .js and .css files, returns the HTML from whatever is in the app.use('/' ...) router. The console returns the following error:
Resource interpreted as Stylesheet but transferred with MIME type text/html: "https://xxx.co.uk/core/styles/m.css".
I experimented with this by changing the homepage to be my contact page, which has resulted in every URL returning the contact page. My CSS and JavaScript are in a public folder. The only thing that I can think of is that the '/' route is somehow being used for every single request, but it doesn't make any sense to me why this could be happening.
Instead of using app.use('/<route>', <router>) in your project, try replacing it with the corresponding express statements, e.g,
router.<route_method>('/<route>', function(req, res, next){
// code
});
See if it works fine or not.
The reason your site is defaulting to the '/' route is because you haven't properly set up your routes. Each router module needs to know what is getting exported. Your indexRouter file, for example, needs to look like this:
var express = require('express');
var router = express.Router;
router.get('/', function(req, res){
res.render('index', { title: 'About', about: true });
});
module.exports = router; <<----
In addition, if the app.js you're showing as of 09/04/2018 06:36 is the complete app.js, you're missing a lot. You have a minimized CSS as your first stylesheet, which actually contains the rendered index.html.

NodeJs routes not working from inner page

I am trying to develop an application in NodeJs using express framework. My routing is working when I navigating from home to inner pages. But If I want to navigate from some inner page to homepage then it is not working.
Below is my app.js code.
const express = require('express');
const path = require('path');
const engines = require('consolidate');
const bodyParser = require('body-parser');
//declare all routers
var home = require(path.join(__dirname, "/routes/index"));
var myaccount = require(path.join(__dirname, "/routes/myaccount"));
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.engine('html', engines.handlebars);
var defaultViewPath = path.join(__dirname, "/views");
app.set('views', defaultViewPath);
app.set('view engine', 'html');
app.use('/', home);
app.use('/myaccount', myaccount);
Here if I have navigated from home to myaccount - Its working
But if I am navigating from myaacount to home - It reloads the same page.
Can anyone help me to resolve this issue.
To define routing using methods of the Express app object, use app.get() to handle GET requests
var express = require('express')
var app = express()
// When GET request is made to the homepage
app.get('/', function (req, res) {
res.render('home');
});
// When GET request is made to the myaccount
app.get('/myaccount', function (req, res) {
res.render('myaccount');
});
app.get('/myaccount/innerpage', function (req, res) {
res.send('Hello Inner Page');
});
//Page Not Found
app.use(function(req, res){
//render the html page
//res.render('404');
res.sendStatus(404);
});
Hope this could help you
use app.get and app.post Route Methods
app.get('/',function(req,res){
res.render('home');
});
app.get('/myaccount',function(req,res){
res.render('myaccount');
});
Or Create Router File For Home & myAccount
var express = require('express')
var router = express.Router()
router.get('/', function (req, res) {
res.send('Home Page')
})
module.exports = router
in Your app.js or index.js file , require route.js
var home = require('./route');
app.use('/', home)

express4 router skipping base route and going directly to a sub-route

I have an express4 router that is skipping the base route and going directly to a designated ./user route. But I can not see why it is skipping the base route.
APP.js
var express = require('express');
var path = require('path');
var logger = require('morgan');
var bodyParser = require('body-parser');
var router = express.Router();
var app = express();
require('./routes')(app);
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
//app.use('/', router);
module.exports = app;
./routes/index.js
var express = require('express');
var router = express.Router();
// route middleware that will happen on every request
router.use(function(req, res, next) {
// log each request to the console
console.log(req.method, req.url);
// continue doing what we were doing and go to the route
next();
});
/* GET home page. */
router.get('/', function(req, res) {
console.log('inside the root route');
});
//module.exports = router;
module.exports = function(app) {
// here we list our individual sets of routes to use in the router
require('./route/user')(app);
};
When I run the app (npm start), console displays the log that resides inside the user route and totally skips the base route ('/').
Where did I go wrong??
It appears that you are only exporting the user routes.
To make this a bit clearer, app.js only has access to the following when you require routes.index.js:
function(app) {
// here we list our individual sets of routes to use in the router
require('./route/user')(app);
};
hence why it is skipping the base route entirely.
Following the API documentation (under the 'express.Router' section), the correct code to export the routes would be:
// exports the router.use and router.get('/) routes
module.exports = router
Which would mean you'd have to include the user routes in another section (such as app.js).

Resources