How to serve Vue App with Express

In this tutorial, I will show you step by step to integrate Vue.js project with Node.js Express Rest API so that we can serve Vue App with Express Server. You will also know how to configure Vue SPA Routing to avoid 404 on refresh.

Related Posts:
Vue.js + Node.js + Express + MySQL example
Vue.js + Node.js + Express + PostgreSQL example
Vue.js + Node.js + Express + MongoDB example
Vue.js + Node.js Express: JWT Authentication & Authorization example

Serverless:
Vue Firebase Realtime Database: CRUD example
Vue Firestore: Build a CRUD App example


Vue.js & Node.js Express Application Overview

Assume that we have 2 separated projects: Vue & Node.js Express like this-

serve-vue-app-with-express-project-structure-separated

For example, if we run them separately:

  • Express Server exports Rest Apis at Url: http://localhost:8080/
  • Vue Client runs at url: http://localhost:8081/

Using Vue.js Client to call Express Rest API:

serve-vue-app-with-express-separated-port

Otherwise, when deploying Vue production-build with Node.js Express project, we only need to run Node Project for the fullstack (Vue + Node) system.

In this example, we access http://localhost:8080/.

serve-vue-app-with-express-same-server-port

Technology Stack

  • Node.js
  • Express
  • Vue 2.6.10
  • Vue Router 3
  • Vue Cli Service 4

Setup Vue Client

You can use your own Vue Project, or just download the source code on Github, or follow these steps to create a new one.

Open cmd at the folder you want to save Project folder, run command:
vue create vue-js-client-crud

You will see some options, choose default (babel, eslint).
After the process is done. We create new folders and files like the following tree:


public

index.html

src

components

AddTutorial.vue

Tutorial.vue

TutorialsList.vue

services

TutorialDataService.js

App.vue

main.js

package.json


Because I want to keep this tutorial simple and brief, please continue to develop this App with instruction in the post:
Vue.js 2 CRUD Application with Vue Router & Axios

Or: Vue 3 CRUD example with Axios & Vue Router

Setup Express Server

You can use your own Node.js Project, or just download the source code on Github, or follow these steps to create a new one.

First, we create a folder:

$ mkdir vue-js-node-js-express-mysql
$ cd vue-js-node-js-express-mysql

Next, we initialize the Node.js App with a package.json file:

npm init

name: (vue-js-node-js-express-mysql) 
version: (1.0.0) 
description: Integreate Vue with Express, Serve Vue app with Express on same Server/Port
entry point: (index.js) server.js
test command: 
git repository: 
keywords: vue, nodejs, express, sequelize, rest, api
author: bezkoder
license: (ISC)

Is this ok? (yes) yes

The instruction can be found in one of the posts:
Node.js Rest APIs example with Express, Sequelize & MySQL
Node.js Rest APIs example with Express, Sequelize & PostgreSQL
Node.js Rest APIs example with Express, Sequelize & MongoDb

Let’s continue to the most important part.

Combine Vue and Express

Build Vue App

Currently Vue Client and Express server work independently on ports 8081 and 8080.

The first thing we need to do is to build Vue App for production.

Open package.json file, modify the scripts so that output files and folders will be stored in build folder:

{
  ...
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build --dest build",
    "lint": "vue-cli-service lint"
  },
  ...
}

Run command: npm run build

PS D:\Projects\VueProjects\vue-js-client-crud> npm run build

> [email protected] build D:\Projects\VueProjects\vue-js-client-crud
> vue-cli-service build --dest build


\  Building for production...Browserslist: caniuse-lite is outdated. Please run next command `npm update`
-  Building for production...

 DONE  Compiled successfully in 6166ms                                                                                                                                           4:54:37 PM
  File                                     Size             Gzipped

  build\js\chunk-vendors.6a5b8348.js       115.12 KiB       40.60 KiB
  build\js\chunk-1b8a9a30.37814895.js      17.45 KiB        6.13 KiB
  build\js\chunk-24926a39.dbd26b18.js      8.95 KiB         3.51 KiB
  build\js\chunk-7d82c670.cf3aa735.js      7.73 KiB         3.25 KiB
  build\js\app.0634cc5c.js                 4.81 KiB         2.07 KiB
  build\js\chunk-29724baf.89c3a7e4.js      2.98 KiB         1.10 KiB
  build\css\chunk-29724baf.1ae50cfb.css    0.05 KiB         0.07 KiB
  build\css\chunk-7d82c670.465f69d9.css    0.04 KiB         0.06 KiB
  build\css\chunk-24926a39.2d561e0d.css    0.04 KiB         0.06 KiB

  Images and other types of assets omitted.

 DONE  Build complete. The build directory is ready to be deployed.
 INFO  Check out deployment instructions at https://cli.vuejs.org/guide/deployment.html

Now you can see new build folder with content as following:

serve-vue-app-with-express-build-folder

Integrate Vue production into Node.js Project

In app folder of Node.js Express Project, let’s create views folder.


app

config

controllers

models

routes

views

index.js

package.json

server.js


Now we need to copy all files from Vue build folder to app/views folder above.

The final Node.js Project folder structure will be like this:

serve-vue-app-with-express-final-project-structure

Serve Vue App with Express

We’re gonna serve static files such as HTML files, CSS files and JavaScript files in app/views folder using the express.static() built-in middleware function.

const express = require("express");

const path = __dirname + '/app/views/';
const app = express();

app.use(express.static(path));

Now, we can load the files in the static directory:

http://localhost:8080/index.html
http://localhost:8080/static/css/2.47e06e2e.chunk.css
http://localhost:8080/static/js/2.8091b331.chunk.js

The final step is to deliver index.html file using res.sendFile(). We will need to pass in a path to the file.

// const path = __dirname + '/app/views/';

app.get('/', function (req,res) {
  res.sendFile(path + "index.html");
});

Now content in server.js file is like this-

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

const path = __dirname + '/app/views/';

const app = express();

app.use(express.static(path));

var corsOptions = {
  origin: "http://localhost:8081"
};

app.use(cors(corsOptions));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

const db = require("./app/models");

db.sequelize.sync();

app.get('/', function (req,res) {
  res.sendFile(path + "index.html");
});

require("./app/routes/turorial.routes")(app);

// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}.`);
});

Run Vue & Express on same Port

Run the Node.js Express server (including Vue client) with commands:
node server.js

Open browser with url: http://localhost:8080/.
Now you can see the result:

serve-vue-app-with-express-demo-same-server-port

Vue Router Refresh – Not found 404 error

Oh yeah! Everything looks good.
But wait, let’s try to refresh the page.
What happened?

serve-vue-app-with-express-vue-router-not-found-404-refresh

To handle this error, we’re gonna re-enable hash(#) in Vue App Routing. It will keep the UI route in hash part of the URL, which should not make the server return 404.

The default mode for Vue Router is hash mode. It uses a URL hash to simulate a full URL so that the page won’t be reloaded when the URL changes. In the source code, we already set Vue Router to history mode to get rid of the hash.

So, in Vue Project, let’s open router.js file and comment or remove mode: "history":

import Vue from "vue";
import Router from "vue-router";

Vue.use(Router);

export default new Router({
  // mode: "history",
  routes: [
    {
      ..
    }
  ]
});

Don’t forget to rebuild the Vue App and copy all files and folders in build directory (Vue project) into views (Node.js Express project).

Now Our Url is hashed(#) after port number: http://localhost:8080/#/tutorials

serve-vue-app-with-express-same-server-port

Now you can refresh the page without worrying about 404.

Conclusion

Today we’ve learned how to integrate Vue with Node.js Express on same Server/Port. We also handle the case Vue Router 404 Not found on Refresh by adding hash(#) to the urls.

There are many Vue + Express examples that you can apply this approach to integrate:
Vue.js + Node.js + Express + MySQL example
Vue.js + Node.js + Express + PostgreSQL example
Vue.js + Node.js + Express + MongoDB example
Vue.js + Node.js Express: JWT Authentication & Authorization example

Or Serverless with Firebase:
Vue Firebase Realtime Database: CRUD example
Vue Firestore: Build a CRUD App example

Happy Learning! See you again.

Further Reading

Source Code

You can find the complete source code for this tutorial at Github.

5 thoughts to “How to serve Vue App with Express”

  1. Thanks for one more wonderful Vue tutorial, I’m a regular reader of your blog. you are good at explanation. I genuinely appreciate you. Keep sharing more posts like this.

  2. Hey, I was wondering if you know how to export the app to desktop app using Electron? I’ve followed your tutorial and I managed to run both the frontend and backend, and now I want to test migrating it to desktop app using Electron.

    Do I need to integrate both the frontend and backend first using this tutorial, and then migrate it to Electron, or should I migrate the Vue app to Electron first, and then integrate it with the backend? Thanks!

  3. Hi bezkoder,

    really fantastic job!!! But what is the advantage of merging vue into express?

    Greetings Daniel

  4. Hi bezkoder

    Excellent article, and you will have a guide or tutorial on how to upload these separate apps in this way backend – frontend to a cPanel hosting.

    .

Comments are closed to reduce spam. If you have any question, please send me an email.