Running frontend and backend on the same port

12,983

Solution 1

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.

Solution 2

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);

Solution 3

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.

Share:
12,983
Eugenio
Author by

Eugenio

Updated on June 05, 2022

Comments

  • Eugenio
    Eugenio almost 2 years

    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.

  • Eugenio
    Eugenio about 6 years
    Thanks a lot for the quick answer! This was exactly what I was looking for, going to try it today and see how it goes!
  • Eugenio
    Eugenio about 6 years
    Thanks for the answer, very straightforward and concise. I am going to try it today!
  • Eugenio
    Eugenio about 6 years
    After a while fiddling with settings and what not I managed successfully to setup a nginx service and redirect correctly to both frontend and backend. Your answer was truly helpful, thanks a lot!