Add custom route in Loopback over nginx proxy

3.3k Views Asked by At

I am attempting to add a custom middleware route to an existing Loopback application, but seeing strange errors using my local domain mapping, but everything works fine on localhost:3000.

My setup is using nginx as a proxy server using

location /api {
  proxy_pass http://localhost:3000;
}

I have added the example server/boot/aroutes.js file from the Add a custom Express Route docs page:

module.exports = function(app) {
  // Install a "/ping" route that returns "pong"
  app.get('/ping', function(req, res) {
    res.send('pong');
  });
}

My server/boot looks like this, so aroutes.js is alphabetically first:

server/boot/
  aroutes.js
  authentication.js
  rest-api.js

My app and the /api same domain proxy work as intended for the app when called by AngularJS, but I can't seem to get a custom Express route to work when called via https://domain.com/api/ping, but it works fine when I use http://localhost:3000/ping.

Using http://localhost:3000/ping I get

enter image description here

But using the full mock domain over the nginx ssl proxy, it does not take the same route:

enter image description here

{
  "error": {
    "name": "Error",
    "status": 404,
    "message": "There is no method to handle GET /ping",
    "statusCode": 404,
    "stack": "Error: There is no method to handle GET /ping
    at restUrlNotFound (/Users/notbrain/src/proteus/node_modules/loopback/node_modules/strong-remoting/lib/rest-adapter.js:332:17)
    at Layer.handle [as handle_request] (/Users/notbrain/src/proteus/node_modules/loopback/node_modules/express/lib/router/layer.js:82:5)
    at trim_prefix (/Users/notbrain/src/proteus/node_modules/loopback/node_modules/express/lib/router/index.js:302:13)
    at /Users/notbrain/src/proteus/node_modules/loopback/node_modules/express/lib/router/index.js:270:7
    at Function.proto.process_params (/Users/notbrain/src/proteus/node_modules/loopback/node_modules/express/lib/router/index.js:321:12)
    at next (/Users/notbrain/src/proteus/node_modules/loopback/node_modules/express/lib/router/index.js:261:10)
    at jsonParser (/Users/notbrain/src/proteus/node_modules/loopback/node_modules/body-parser/lib/types/json.js:96:40)
    at Layer.handle [as handle_request] (/Users/notbrain/src/proteus/node_modules/loopback/node_modules/express/lib/router/layer.js:82:5)
    at trim_prefix (/Users/notbrain/src/proteus/node_modules/loopback/node_modules/express/lib/router/index.js:302:13)
    at /Users/notbrain/src/proteus/node_modules/loopback/node_modules/express/lib/router/index.js:270:7"
  }
}

Is this expected behavior? How can I get the two to be equivalent? Seems like two issues here

  1. nginx proxy_pass is interfering with loopback routing
  2. my custom route is not registered with loopback when nginx interferes

UPDATE 6/24: More explicit nginx config used here:

http {

  # ...snip logging etc... #

  upstream api {
    # loopback api on port 3000
    server localhost:3000;
  }

  upstream app {
    # browser-sync frontend for dev on port 3001
    server localhost:3001;
  }

  map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
  }

  server {
    listen       443 ssl;
    server_name  dev.app.proteus.com;

    ssl_certificate      ssl/dev.app.proteus.com.crt;
    ssl_certificate_key  ssl/dev.app.proteus.com.key;

    ssl_session_cache    shared:SSL:1m;
    ssl_session_timeout  5m;

    # ----- PFS ----- #
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";

    location / {
      # proxy_pass to connect server spawned by gulp browserSync ------------
      proxy_pass http://app;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "Upgrade";
    }

    location /api {
      proxy_pass http://api;
    }

  }

}
2

There are 2 best solutions below

0
On

This does not appear to be a Loopback specific issue, but more related to Nginx and how its setup. To verify this, try creating a simple Hello World app in Node.js to see if you get the same error.

3
On

You may want to use the upstream in nginx config.

In your loopback config for that environment, change host parameter to server name. Then in your nginx config upstream, instead of localhost:{port}, change to the same server name.

For example in server/config.test.js for test environment (export NODE_ENV=test before starting server), you could override the default host:

module.exports = {
    host: "app3",
    ...
}

Then in your etc/nginx/conf.d/{app-name}.conf file (good to create one per app), you can have something like this:

upstream my-app {
    server app3:3000;
}

server {
    listen 80;
    server_name your-server-domain.com;

    # custom handlers for internal apps
    # add stuff as needed below
    #
    location ^~ / {
        proxy_set_header HOST $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://my-app;
    }
}

Now you could always change the location param to not be global and just use /api but same handling. The trick is the Loopback config host param must match the host declared in your upstream of nginx config.