Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running frontend and backend on the same port

I am having a problem today that has something to do with routing. I have two main codes: one is the frontend and the other one is the backend.

The frontend is written using Vue.js so it's a SPA. This webapp is kind of complex and involves a lot of routing and backend AJAX API calls.

// All imports
import ...

loadMap(Highcharts);
loadDrilldown(Highcharts);
boost(Highcharts);

Vue.config.productionTip = false

Vue.use(VueCookie);
Vue.use(ElementUI, {locale});
Vue.use(VueRouter);
Vue.use(VueHighcharts, {Highcharts });
Vue.use(HighMaps);

// This is a global component declaration
Vue.component('app-oven', Devices);
Vue.component('app-sidebar', SideBar);
Vue.component('app-header', Header);
Vue.component('app-footer', Footer);
Vue.component('app-query', Query);
Vue.component('app-deviceproperties', DeviceProperties);
Vue.component('app-device', Device)
Vue.component('app-queryselection', QuerySelection)
Vue.component('app-index', Index)
Vue.component('app-index', Error)
Vue.component('app-realtime', RealTime);
Vue.component('app-login', Login)
Vue.component('app-preferences', Preferences)

const routes = [
  { path: '/index', component: Index},
  { path: '/', component: Login},
  { path: '/device/:deviceId', component: Device},
  { path: '/preferences', component: Preferences},
  { path: '*', component: Error}
];

const router = new VueRouter({
  routes: routes,
  mode: "history" // Gets rid of the # before the path
})

new Vue({
  el: '#app',
  router: router,
  components: { App },
  template: '<App/>'
})

The backend is written using Express on Node.js and it answers to specific AJAX calls from the Frontend.

// All imports
import ...

function prepareApp() {
    let app = new Express();

    app.use(cors({
        origin: "*",
        allowedHeaders: "Content-type",
        methods: "GET,POST,PUT,DELETE,OPTIONS" }));

    app.use(function(req, res, next) {
        res.header("Access-Control-Allow-Origin", "*");
        res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        next();
    });

    app.use(helmet());
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({extended: false}));

    // Get all parameters
    app.get('/params', params.parameters);
    // Get all devices ever seen on the databases
    app.get('/devices', params.devices);

    app.get('/organizeData', organizer.updateAll);

    // WebApp used services to access various things
    app.post('/customQuery', stats.query);
    app.post('/statistics', stats.statistics)
    app.post('/getUserInfo', stats.getUserInfo)
    app.post('/setUserInfo', stats.setUserInfo)
    app.post('/genericQuery', stats.genericQuery)
    app.post('/NOSQLQuery', stats.NOSQLQuery)

    // Users check and insertion
    app.get('/insertUser', stats.insertUser)
    app.post('/verifyUser', stats.verifyUser)

    app.get('/', errors.hello); // Returns a normal "hello" page
    app.get('*', errors.error404); // Catch 404 and forward to error handler
    app.use(errors.error); // Other errors handler

    return app;
}

let app = prepareApp();

//App listener on localhost:8080
app.listen(8080, () => {
    console.log("App listening on http://localhost:8080");
});

I only used this setup during development so I had both running at the same time on localhost with a different port for both. Now I would like to start the production cycle but I have no idea where to start.

Most importantly I am deploying both applications onto a Virtual Machine that is running on an external server. It already has a DNS association and a static IP address so that is already covered. The problem arises when I try to run both programs at the same time on this production machine since its open ports are only the port 80 and the port 443. I think this is pretty normal in a production environment but I don't know how to adapt my applications so that they can still talk to each other and retrieve useful information from the Database while still using a single port.

I hope I explained the problem kinda well. Looking forward to a nice (and maybe long) answer.

like image 411
Eugenio Avatar asked Apr 11 '18 15:04

Eugenio


3 Answers

I'd recommend running the backend on port 3000 internally and have nginx listening on 80 and 443 and proxying urls starting with '/api' to 3000 and deliver the frontend directly since it's just a bunch of static files.

This would be your nginx configuration. Just make sure backend server has some api prefix like '/api'. Build your vuejs app with 'npm run build' and copy the folder to /opt/frontend.

upstream backend {
    server 127.0.0.1:3000;
}

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    location /api/ {
        proxy_pass         http://backend;
        proxy_redirect     off;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Host $server_name;
    }

    location / {
        root /opt/frontend/dist;
        try_files $uri $uri/ /index.html;
    }
}

Alternatively, you could use the backend to host the frontend. However, a webserver like nginx is more efficient at serving static files than your backend api server.

like image 148
Kris Avatar answered Nov 14 '22 05:11

Kris


If you don't have a way to open more ports, you can build your frontend into production mode and then take its index.html and dist folder to the same folder where your nodejs app are. Then you create a express app listening to port 80 and send the HTML file.

var express = require('express');
var app = express();
var path = require('path');
var dir = '//vm//path//here';

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

app.listen(80);
like image 36
gbarzagli Avatar answered Nov 14 '22 03:11

gbarzagli


In my case, my backend server doesn't run in cluster mode( e.g. with 3001, 3002... together with 80 port)

My case: rails server running with passenger ( mydomain.com , 80 port ) and I need to run my Vuejs project with the same domain ,the same port.

so the only solution is to run vue in specified URL.

this is my solution:

1.change your nginx config.

http {
    # our backend app is passenger( rails server, running on 80 port)
    passenger_root /usr/local/rvm/gems/ruby-2.2.10/gems/passenger-6.0.0;
    passenger_ruby /usr/local/rvm/gems/ruby-2.2.10/wrappers/ruby;

    include       mime.types;
    default_type  application/octet-stream;

    server {
     listen 80;
     passenger_enabled on;
     # we are using this folder as the root of our backend app.
     root /mnt/web/php/public;
     charset utf-8;

     location ~ ^/(images|javascripts|stylesheets|upload|assets|video)/  {
       root /mnt/www/php/public;
       expires 30d;
       add_header Cache-Control public;
       add_header ETag "";
     }

     # vuejs related content
     location /vue.html {
       root   /mnt/web/vuejs/h5/dist;
     }
     location /static {
       root   /mnt/web/vuejs/h5/dist;
     }
    }
}

2.in your vue project's dist folder:

$ mv index.html vue.html

3.all the requested url in your vuejs project should be changed according to the nginx config.

like image 1
Siwei Avatar answered Nov 14 '22 03:11

Siwei