How can a node.js express application be deployed behind HAProxy? - node.js

I have a node.js express application running on machine1 on port 3000. I can successfully access my application via machine1:3000
I have HAProxy running at machine2. I updated haproxy.cfg as follows
frontend main *:80
acl url_mynodeapp path_beg -i /mynodeapp
use_backend mynodeapp if url_mynodeapp
backend mynodeapp
mode http
reqrep ^([^\ ]*\ /)mynodeapp[/]?(.*) \1\2
balance roundrobin
server machine1 1.1.1.1:3000
I can now hit my application at machine2/mynodeapp. However all relative links are now broken, including css and javascript (since they point to machine2 instead of machine2/mynodeapp).
How do you deploy an express application behind a proxy? How do you deploy an express application with a subfolder? I am coming from the java world were all web applications are name spaced inside the servlet container, thus giving each application its own subfolder.
Thanks,
Nathan

/mynodeapp isn't a subdomain. It's a subfolder. Subdomain would be mynodeapp.machine2
Subdomain hosting works fine with express, since that is not express's job, rather the front-end's.
As long as your front-end resolves consistently, (which it would with subdomains, not subfolders), express will receive expected urls from HAproxy

The only solution I could find was manually setting up the node application to run from a subfolder.
I used the solutions provided from How to handle relative paths in node.js / express?
and call functions from with ejs templates on node
var subfolder = '/mynodeapp';
app.set('view engine', 'ejs');
app.use(subfolder, app.router);
app.use(subfolder, express.static(__dirname + '/public'));
app.locals.createLink = function(uri) {
return subfolder + uri;
}
Then in ejs views, create links via function call createLink.
<script src="<%= createLink('/javascript/myjavascript.js') %>">
Home
Now I have a node.js express application running on at machine1:3000/mynodeapp.
I can then easily route thru a proxy and access my application at machine2/mynodeapp. Note: in the proxy config, just remove the regrep line.

Related

Deploying dynamic Nextjs + Nodejs application inside docker using caddy server

I am currently developing a simple portfolio app and my app structure is like this.
Nextjs/client,
Nodejs/server,
Mongodb/db
Nextjs is hosted locally on port 3001, Nodejs app on 5000. Whenever nextjs needs to fetch any api it calls nodejs application. All the things are configured inside docker. I am very new to deploying nextjs application and have recently used caddy server which has automatic https.
I am able to deploy the nextjs application statically using commands
next build
next export
The statically exported file called index.html inside out directory of nextjs application is pointed to caddy server on port 80 and 443. Statically exported app doesn't support api routes which I recently came to know. I tried next build and next start command to generate a dynamic production build inside .next directory. The main problem is How do I point my dynamically generated nextjs application in caddy configuration inside docker container. My present caddy configuration looks like
www.example.com:443 {
tls xyz#email.com
root * /srv
route {
reverse_proxy /api* api-server:5000
try_files {path} {path}/ /index.html
file_server
}
}
I am looking for hints especially related to proxy server.
Thank you in advance
I'm assuming the api url in your frontend looks like this http://localhost:5000 (based on your youtube comment here) which won't work if you're accessing your dockerized app from a remote computer (in this case your computer, since I'm assuming your app is hosted). Try changing it to https://www.example.com:5000 and rebuild your image.

Namecheap: Node JS Express App - App Route return 404 not found

Trying to get Simple Express Application up using NameCheap Shared Hosting.
I have set up my Node JS application as Described here NodeJS NameCheap Docs
Current Setup:
Application Root: url.com
Application URL: url.com
Application Startup File: server.js
I have ran NPM Install using the button provided
I have tried loading the URL http://url.com/hello Expecting Hello World to displayed in the Page.
var express = require("express");
var app = express();
const port = 3001;
app.set("port", port);
app.get("/hello", function(req, res) {
res.send("hello world");
});
app.listen(app.get("port"), () =>
console.log("Started listening on %s", app.get("port"))
);
The results I am getting when navigating to http://url.com/hello:
Not Found
The requested URL /index.php was not found on this server.
Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.
Namecheap only tells you how to configure the nodejs app however their hosting is based on cPanel which requires you configure the webserver (apache generally). Once you get an application running there’s a special button to register it for the apache configuration aka let it run from your domain. I don’t know the steps by heart but you should ask NC support to direct you to their documentation for configuring apache to run a nodejs app you configured.
If they do not link an article from their knowledge base use this link: https://confluence1.cpanel.net/plugins/servlet/mobile?contentId=17190639#content/view/17190639
Basically what you need now is to configure cPanel or ssh into your server and test your app locally. There’s a number of things that could cause your issues like incorrect apache configuration (your default port 80 is looking for php app), port not open/firewalled, application not registered - and all of this is cPanel specific.
To make sure you are reading the correct document check in namecheap cpanel for the docs button and review all the above. It should be obvious what needs configured - your nodejs code is probably not the cause here
In my case, it was the problem with .htaccess file. Adding the following rules in my .htaccess file present in the website's public directory helped me:
# CLOUDLINUX PASSENGER CONFIGURATION BEGIN
PassengerAppRoot "/home/<user>/<your_nodejs_app_folder>"
PassengerBaseURI "/."
PassengerNodejs "/home/<user>/nodevenv/<nodejs_app>/<version>/bin/node"
PassengerAppType node
PassengerStartupFile <startup_script>.js
# CLOUDLINUX PASSENGER CONFIGURATION END
Make the required changes in the above rules before pasting them in your .htaccess file. Also, just in case, make sure the port you are using is open, via customer support.

NGINX setup for separate Vue Frontend and Express Backend

I am wondering about the proper nginx setup when deploying a vue frontend separately from an express backend - not separately in terms of servers or domains, but in terms of how they are served.
During development, I use npm serve in the vue directory, and to build a production build, it is generated via npm run build. The resulting dist folder should be served, and my question is how this is done when the backend is on the same server.
Let's say for the backend, express is exposing routes. Should nginx be in front of express here?
The vue front end is calling those routes, but the static files need to be served. According to the docs this can be done using serve. Is this intended for production? And then again, should nginx be in front of this?
I am wondering, because the route would then be:
Browser Request -> Nginx to Vue Frontend -> Vue Frontend -> Nginx to
Backend
Is this a suitable approach or am I misunderstanding this?
Should nginx be in front of express here?
Yes, it is a very good idea.
You have to use a distinct set of URLs for Vue and Express, so Nginx, while looking at request URL, will be able to understand what to do: give a Vue file or proxy to Express. Nginx has a variety of options how to classify incoming requests: by different hostnames, by paths, by combination of both, etc.
For example, prepend all your Express routes with /api/ path prefix. Then configure nginx like this:
This is not production ready configuration, I'm just trying to give a hint what you should look for in nginx docs
server {
listen 80;
server_name mydomainname.com;
location /api {
proxy_pass http://localhost:8000; # port that Express serves,
# better change to UNIX domain socket
}
location / {
root /vue_root/dist;
}
}

How to run Node Express server and Angular on the same port?

I am new to Node and Angular. I need to know whether is it possible to run a Node Express app serving as a backend and an Angular frontend on the same port. I followed Angular Quickstart tips on angular.io and created a Node todo application but both are running on different port which raises the issue of Cross Origin Request Blocked Issue.
To have Node.js serve the Angular app on the same port, your Angular app must be deployed under your Node's directory where the static resources are deployed. But in dev mode, it's more productive to serve your Angular bundles (so they auto-rebuild in memory as you code) from the dev server, e.g. on port 4200, while the Node server runs on another port, e.g. 8080.
To avoid cross-origin issues, you need to configure a simple proxy file in your Angular app to redirect all data requests to your Node server. For example, create a file proxy-conf.json in the root dir of your Angular project:
{
"/api": {
"target": "http://localhost:8080",
"secure": false
}
}
This will redirect all requests that have /api in the URL to your Node server, assuming that it runs on port 8080. Then start your Angular app using the following command:
ng serve --proxy-config proxy-conf.json
An HTTP request in your Angular App can look like this:
http.get('/api/products');
Of course, you need to configure the /api/products endpoint for GET requests on your Node server.
To get Angular and Express running on the same port I've always served my Angular build files by the Express app itself. You should be able to tell Express to serve static content from an Angular build directory like this:
app.use(express.static('../accounting-client/dist'));
Which would work if you had a file structure like so and were running serve.js with Node:
-accounting-server
-serve.js
-accounting-client
-dist/*
You can customize as needed by configuring the Angular build folder to be wherever you need it, or use Grunt/Gulp to move files around to the folders you prefer with a build task.
As mentioned by Yakov this isn't ideal for development since it won't work with the Angular dev server's auto-refresh.
The fact that you need to have access to your client-side project from within Express project, as spacefozzy said, is true. but you still can keep your projects separated.
To do so, you can create a symlink from your client-side project directory in your Express project directory:
// while in Express directory
ln -s ~/path/tp/client-side/build name-in-espress-dir
This way you can maintain projects isolated.

socket.io cannot communicate with back-end through express

I am using socket.io for fetching some data from the express app. Everything works fine on localhost:8080 but when i deploy my code to the server, client side socket.io cannot communicate with back-end socket.io through express server. I've an apache on the server that forwards everything to localhost:8080 including domain.com/socket.io/?transform=polling... and it extends the request to the express server but express returns a 404 (it comes from the express, not from apache). I am out of ideas, what could be the problem? Here is my code:
express = require "express"
jade = require "jade"
fs = require "fs"
class Bootstrap
_self = undefined
routes:
DEFAULT_PATH: "/"
TEMPLATE_PATH: "/load/:view"
DIRECTIVE_PATH: "/directive/:template"
options:
templatePath: "#{__dirname}/../src/templates"
isDev: "#{__dirname}/../dev"
contentPath: "#{__dirname}/../frontend"
libraryPath: "#{__dirname}/../bower_components"
port: 8080
status:
notFound: 404
isDev: undefined
constructor: ->
_self = #
#isDev = fs.existsSync #options.isDev
#app = express()
#app.use "/frontend", express.static(#options.contentPath)
#app.use "/bower_components", express.static(#options.libraryPath)
#app.set "views", #options.templatePath
#app.set "view engine", "jade"
#app.engine "jade", jade.__express
#app.get #routes.DEFAULT_PATH, (request, response)->
appData =
data:
isDev: _self.isDev
response.render "index", appData
#app.get #routes.TEMPLATE_PATH, (request, response)->
view = request.param "view"
response.render view
#app.get #routes.DIRECTIVE_PATH, (request, response)->
template = request.param("template").replace ".html", ""
response.render "directives/"+template
#app.use (request, response, next)->
_self.logger.warning "404 Not Found!: " + request.originalUrl
response.status(_self.options.status.notFound)
appData =
data:
isDev : _self.isDev
request: request
response.render "404", appData
#server = #app.listen #options.port
#io = require("socket.io").listen #server
#logger = require("./logger.js")
#logger.init #isDev
#socketConnector = require("./live.js")
#socketConnector.init #io
#
new Bootstrap()
You can find the entire code here: https://github.com/eyurdakul/ejder.be
Since you specifically said it works locally and I don't see anything special in your code, I really think the issue resides in your Apache config.
There is a module called mod_proxy_wstunnel you need to have and enable in order to get your WebSocket traffic to work and get proxied correctly to your express application. Either you have Apache 2.4 or, you'll need to apply this patch found in this blogpost and compile again. There is also another blog detailing step by step what you should do under Ubuntu.
After making sure you have the module and its loaded, you'll have to tweak your config to add some WebSocket proxying using the ProxyPass directive
ProxyRequests Off
ProxyPass "/socket-io/" "ws://localhost:8080/socket.io/"
ProxyPassReverse "/socket-io/" "ws://localhost:8080/socket.io/"
You might also want to try to use a rewrite condition on the Upgrade header that is sent with WebSockets:
RewriteEngine On
RewriteCond %{HTTP:Connection} Upgrade [NC]
RewriteRule /(.*) ws://localhost:8000/$1 [P,L]
ProxyPass / http://localhost:8000/
has seen in this comment or this specific thread in general, which exposes a few other possible solutions you can try.
A debugging step that you should take is to have a mock client run in your express app and try to connect to the server port using localhost, or whatever the loopback IP address is. In other words (purely as a temporary testing tool) you place a socket.io Node client into your Express app that gets instantiated and immediately tries to connect to the Socket.io server (which is also in the express app). This should work.
If it does work, you've established that your express server is not the problem. Then, on the client you just added to your express app, you should change the connection address from the local IP to the actual IP address of the server.
If it doesn't work, your problem is probably server side.
My guess is that your apache server is forwarding the polling requests properly, but when Socket.io tries to make the handoff to websockets, Apache is denying the WS request and socket.io is handling this denial internally -- which is why the 404 seems to come from express.
Even I had same issue. The port on which the server is listening , is that open on server. As on local all the ports are open but on server , it's not the case.

Resources