[Fixed] NodeJs – VueJs – Cannot GET /routes in production

Issue

I want to put in production my website developped in NodeJs (express) and VueJs (2.6.11).

My Folder look like that :

/MyNodeJsApp
     - MyVueJsApp

I have this in my vue.config.js

outputDir: path.resolve(__dirname, "../public")

So when I npm run build it goes on a public directory at the root of my project.

My server.js is like that :

const bodyParser = require("body-parser");
var cors = require('cors')

require('rootpath')();
const basicAuth = require('app/helpers/basic-auth');
const errorHandler = require('app/helpers/error-handler');
const app = express();

// parse requests of content-type: application/json
app.use(bodyParser.json());

app.use(express.static(__dirname + '/public'));

// parse requests of content-type: application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));

// Allow cors
app.use(cors())

// use basic HTTP auth to secure the api
app.use(basicAuth);

// global error handler
app.use(errorHandler);

// all Page routes
require("./app/routes/pageitem.routes.js")(app);
require("./app/routes/actualite.routes.js")(app);
require("./app/routes/user.routes.js")(app);
require("./app/routes/contact.routes.js")(app);

//We'll use the public directory to serve the Vue app
app.use(express.static('public'));

// set port, listen for requests
const port = 3000;
const server = app.listen(port, function () {
    console.log('Server listening on port ' + port);
});

All the routes in my Nodejs api contain the same prefixe /api/ like so :

module.exports = app => {
    const actualites = require("../controllers/actualite.controller.js");
  
    // Retrieve all Pages
    app.get("/api/actualites", actualites.findAll);

    app.get("/api/actualites/type", actualites.getAllType);

    app.get("/api/actualites/type/:type", actualites.findFromType);

    // Retrieve a single Page with customerId
    app.get("/api/actualites/:id", actualites.findOne);
  };

This is my VueJsapp/router/index.js :

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Missions from '../views/Missions.vue'
import VousEtes from '../views/VousEtes.vue'
import VousEtesDesc from '../views/VousEtesDesc.vue'
import Actualite from '../views/Actualite.vue'
import Actualites from '../views/Actualites.vue'
import Service from '../views/Service.vue'
import Contact from '../components/Contact.vue'
import LoginPage from '../views/LoginPage.vue'
import Recrutement from '../views/Recrutement.vue'


Vue.use(VueRouter)

const routes = [
  {
    path: "/",
    redirect: "/home"
  },
  {
    path: '/home',
    name: 'Home',
    component: Home
  },
  {
    path: '/missions',
    name: 'Missions',
    component: Missions
  },
  {
    path: '/recrutement',
    name: 'Recrutement',
    component: Recrutement
  },
  {
    path: '/vousEtes',
    name: 'VousEtes',
    component: VousEtes
  },
  {
    path: '/vousEtesDesc/:id',
    name: 'VousEtesDesc',
    component: VousEtesDesc,
    props(route) {
      return {
        pageid: route.params.id
      };
    },
  },
  {
    path: '/service',
    name: 'Service',
    component: Service
  },
  {
    path: '/actualites',
    name: 'Actualites',
    component: Actualites
  },
  {
    path: '/actualite/:id',
    name: 'Actualite',
    component: Actualite,
    props(route) {
      return {
        newsId: route.params.id
      };
    },
  },
  {
    path: '/contact',
    name: 'Contact',
    component: Contact
  },
  {
    path: '/login',
    name: 'Login',
    component: LoginPage
  }
]

const router = new VueRouter({
  mode: 'history',
  scrollBehavior () {
    return { x: 0, y: 0 }
  },  
  routes
})

export default router

When I go to my localhost:3000 the website is working fine, I can navigate threw the website, every pages is loaded correctly with the right data. So my VueJs front is working fine.

But when I try to refresh a page, I get Cannot GET /pagename

I’ve try multiples solutions, changing my routes names, changing the way I import them in my server.js, I always get the same error.

Solution

As per the documentation

When using history mode, the URL will look "normal," e.g.
http://oursite.com/user/id. Beautiful!

Here comes a problem, though: Since our app is a single page client
side app, without a proper server configuration, the users will get a
404 error if they access http://oursite.com/user/id directly in their
browser. Now that’s ugly.

Not to worry: To fix the issue, all you need to do is add a simple
catch-all fallback route to your server. If the URL doesn’t match any
static assets, it should serve the same index.html page that your app
lives in. Beautiful, again!

To fix this

For Node.js/Express, consider using connect-history-api-fallback middleware.

Follow the documentation

To install the plugin

npm install --save connect-history-api-fallback

Import library

var history = require('connect-history-api-fallback');

Add as a middleware

var express = require('express');

var app = express();
app.use(history({
    index: '/home.html'
});

Leave a Reply

(*) Required, Your email will not be published