Node.JS + Javascript and Nginx are the most popular stack that most busiest sites and APIs are using or considering to use. Especially Nginx is threatening other web servers and Node.JS / Javascript are huge hypes around web developers.

My type of guys - software development and open source addict - won’t miss such a trend, so I have started developing an application which is a SaaS for load balancing your applications. In its core, I am using Node.JS to manage Nginx configurations.

Nginx is not used as a web server especially for static content and cgi, it is also one of the best load balancer (reverse proxy) in the market.

I will share a core npm package that I have recently made public for managing nginx configuration file with nodejs. This will be the first part of this series and I will continue on more details and updates over this package. Please fork me from github to contribute, since there are lots of nginx functionality that needs to be added.

Node.js and Nginx

Nginx As Reverse Proxy

Before going into details of npm packege, let’s take a look at nginx configuration for setting up a reverse proxy. Nginx configuration file under Debian (Ubuntu) is located under /etc/nginx/nginx.conf. This file is the global configuration for all of your virtual hosts that are hosted by nginx. Generally just closer to the end of this file, you would see the included virtual hosts include as something like below;

1
2
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

This is where nginx load extra configuration for virtual hosts. A simple configuration for a load balancer would look like this;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Upstream Servers
upstream nginxconf {
server localhost:81;
server localhost:82;
}
server {
# Ports
listen 80;
# Server (FQDN)
server_name example.com;
# GZIP
gzip off;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gunzip on;
# Location
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
add_header Cache-Control "no-store";
proxy_pass http://nginxconf;
proxy_next_upstream error timeout http_502 http_504;
proxy_redirect off;
# Handle Web Socket connections
proxy_http_version 1.1;
}
}

Here our server listens 80 port and if a request to this server comes for fqdn : example.com, than this block configuration would be valid for our nginx server. Here we are proxying (balancing) all requests for this server by mentioning / in location block. location /

Our backend servers are again in this server listening ports 81 and 82.(Generally, the backend application servers would be on a different server) and they are mentioned in the upstream block.

1
2
3
4
5
# Upstream Servers
upstream nginxconf {
server localhost:81;
server localhost:82;
}

I would not go into details for now, but if you are curious about the great features of nginx visit here.

nginx-upstream NPM package

Back to programming, phew.. , so how nginx-upstream package can help you to deal with the configuration on the fly. Currently it is on release v0.1.3 and maintains primary functionality over nginx config file. Usage is pretty simple, you can use it with require to get our main class and use its instance with below constructor parameters.

1
npm install nginx-upstream --save
1
2
var NginxManager = require('nginx-upstream');
var nginxManager = new NginxManager('<path>/nginx.conf', 50);

First of all, let’s take a look at the type definition of the package starting from constructor.

1
2
class NginxUpstream {
constructor(nginxConfigFilePath: string, fileSyncTime?: number);

As expected our class initializes with the nginx config file path as first parameter. Second parameter is the timeout for waiting changes on config file. Default is 50ms and it is generally ok for nowadays disks.

Nginx requires upstream block for load balancing operations. You can add new backend to your upstream block by addBackend method. Below is the definition of it;

1
addBackend(host: string, callback: (err: any) => void);

addBackend method requires the host definition together with the port where the backend application server exits. This host definition can be either like ip:port or like fqdn:port. ie. 123.45.67.89:80 or www.example.com:81

Here port definition is always required even if your backend server is a web server and by default hosts over 80 port, you need to mention this to your Nginx server and our method also requires this port information to set nginx configuration file correctly.

Second parameter is callback, where you need to provide to understand if the configuration set successfully or not. Usage example;

1
2
3
4
5
6
7
8
nginxManager.addBackend('mybackendserver.io:8081', function(err){
if(err){
console.log(err);
return;
} else {
// Do something after backend server added.
}
});

Another method is of nginx-upstream is toggleBackend, which is for enabling or disabling your backend server. This is realy useful when you want to make maintenance on one of your backend server or something is wrong with it and you want it to be disabled temporarily. Even if you want to remove your backend server than it is wise to disable it first and than remove it.

Below is the definition and usage of toggleBackend;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Definition
toggleBackend(host: string, callback: (err: any, status: boolean) => void);
// Usage
nginxManager.toggleBackend('localhost:81', function(err, status){
if(err){
console.log(err);
return;
} else if(status)
// Do something if status enabled.
} else {
// Do something else if status disabled.
}
});

It is like add backend and it requires the host name together with its port. When you toggle your backend host “localhost:81” of this configuration.

1
2
3
4
5
# Upstream Servers
upstream nginxconf {
server localhost:81;
server localhost:82;
}

the nginx configuration file would be like below;

1
2
3
4
5
# Upstream Servers
upstream nginxconf {
server localhost:81 down; # Disabled backend
server localhost:82;
}

Ofcourse we have another method for removing the backend server from the nginx configuration file. It goes like below for the same upstream block;

1
2
3
4
5
6
7
8
9
10
// Definition
removeBackend(host: string, callback: (err: any) => void);
// Usage
nginxManager.removeBackend('localhost:81', function(err){
if(err){
console.log(err);
return;
} else
// Do something after backend server removed.
});

After calling the removeBackend method, upstream block would be;

1
2
3
4
# Upstream Servers
upstream nginxconf {
server localhost:82;
}

So in this blog post, I have started to describe the nginx-upstream and as I mention, I will continue on the next series. Thanks for reading.

Fork me on github to contribute github