A PHP developer's first journey into the NodeJS land! (and bumping into a CORS issue)
Tags: node, sails, vue, nginx
Published: 2019-08-21 13:43:00

I am a long time LAMP developer, PHP being my main language for many years.
Lately I was faced to choose a different stack for a project which involved the creation of a website, a backoffice for it and the corresponding IOS/Android mobile apps.
I am the kind of guy that takes months before deciding which technology/framework to turn to next and usually I am spending a lot of time googling and reading characteristics, features, comparisons and reviews.

After much pro and cons weighting I have decided for:
- Nodejs, because of the assumed speed and the huge number of free packages;
- Sailsjs as a nodejs framework, because it seemed more like the MVC model I was used to in the PHP land;
- Vuejs, easier to grasp and not dependent of a tech company;
- Mysql instead of the ubiquitous mongo, because I like relational databases and used them for so long.
- nativescript for the mobile apps, because well, you guess, it is the closest to being native without really using the native API's directly (this is still in early works as I have just barely begun with it).

One of the main reasons for this stack was the fact that I could use a single language, Javascript. As you might guess, my previous interraction with JS was mainly by using the Jquery library as any PHP developer can tell.
Getting re acquainted and updated with JS was no pain compared with the new tech I had to study.

Being  a new stack for me I had to take a few Udemy courses, read a lot and start with the the usual grinding trial and error every developer knows so well.

Having ongoing work for my regular projects, I have agreed with the client that I'll allocate on this project up to max 50% of my working hours so it is no wonder it took me almost 6 months to deliver the webapp and a backoffice for it.

This amount of time includes the learning, developing, testing and bug fixing so I expect that on a next similar project I'll be able to reduce this time to no more than half. 

I have built the webapp with Sails and its particular Vue wrapper (parasails).
The backoffice is a separate webapp using the same API created with Sails but this time I have used plain Vuejs for the backend frontend, so to speak.

When it came to install everything for the client I chose an ec2 instance. Although some might argue there are maybe better approaches to it, as a Linux guy I thought I should stick with the server tech I new better, no time to further experiments.

Because this server is meant to run several apps my choice was to configure Nginx as a reverse proxy and sent the requests to PM2.

Easier said than done because despite reading everything I could for such a setup I was caught in a CORS hell where nothing seemed to work in terms of accessing the Sails API from the backend app.

That is why I think I should write down the final working configuration of this app serving setup as I have spent lots of days trying to make it work.

I hope it'll avoid others the frustration I endured during this time when nothing seemed to work for me although it apparently worked for others.

So here it is:
1. Nginx configuration for the sails website, living in the nginx sites-available directory:
```
server {
    listen 80;
    server_name example.com;
    return 301 https://example.com$request_uri;
}

server {
    server_name example.com www.example.com
    root /usr/share/nginx/example.com;

    location / {
    include /etc/nginx/cors.conf;

    proxy_http_version 1.1;
    proxy_pass http://app_nodes;
    proxy_redirect off;
    proxy_pass_header  Set-Cookie;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    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-NginX-Proxy true;
    proxy_pass_header X-XSRF-TOKEN;
    proxy_read_timeout 240s;
   }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot


}

server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80 default_server;

    server_name example.com www.example.com;
    return 404; # managed by Certbot
}
```

A similar second configuration is necessary for proxying to the backend Vue app which connects to the Sails API.

2. The included cors file living in the nginx root directory:

```
set $cors_origin "";
    set $cors_cred   "";
    set $cors_header "";
    set $cors_method "";

    if ($http_origin ~ '^https?://(localhost|www\.allscubashops\.com|XXX.XXX.XXX.XXX)') {
            set $cors_origin $http_origin;
            set $cors_cred   true;
            set $cors_header $http_access_control_request_headers;
            set $cors_method $http_access_control_request_method;
    }

    add_header Access-Control-Allow-Origin      $cors_origin;
    add_header Access-Control-Allow-Credentials $cors_cred;
    add_header Access-Control-Allow-Headers     $cors_header;
    add_header Access-Control-Allow-Methods     $cors_method;

```

The "XXX.XXX.XXX.XXX" stands for the Vue backend running in dev mode in order to be accepted to connect to the Sails API.

3. Finally, the cors setting in the Sails config/security.js"
```
cors: {
    allRoutes: true,
    allowOrigins: [
      'http://XXX.XXX.XXX.XXX:443',
      'https://example.com',
    ],
    allowRequestMethods: 'GET, POST, PUT, DELETE, OPTIONS, HEAD',
    allowRequestHeaders: 'Content-Type, Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With',
    allowCredentials: true,
  }
```

That's all for the moment, hope it helps someone.