@stonyx/rest-server 0.2.1-alpha.3 → 0.2.1-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,6 +51,7 @@ From `config/environment.js`. All values are overridable via environment variabl
51
51
  | Option | Type | Default | Env Var | Description |
52
52
  |---------------------|-------------------|-------------------------------|----------------------------|-----------------------------------------------------------------|
53
53
  | `enableHealthCheck` | **Boolean** | `true` | `REST_HEALTH_CHECK_DISABLE=true` to disable | Registers `GET /health` returning 200 |
54
+ | `trustProxy` | **Boolean** | `false` | `REST_TRUST_PROXY=true` to enable | Trust reverse proxy headers (`X-Forwarded-Proto`) for correct protocol detection behind load balancers |
54
55
  | `origin` | **String** | `'*'` | `REST_CORS_ORIGIN` | CORS allowed origin(s) |
55
56
  | `methods` | **String** | `'GET,POST,PATCH,PUT,DELETE'` | `REST_CORS_METHODS` | CORS allowed methods |
56
57
  | `dir` | **String** | `'./requests'` | `REST_REQUEST_PATH` | Directory containing Request class files to mount as routes |
package/README.md CHANGED
@@ -75,8 +75,21 @@ Configuration is read from `stonyx/config` under `restServer`:
75
75
  | `origin` | **String \| Array** | `'*'` | CORS origin(s) allowed |
76
76
  | `methods` | **String** | `'GET,POST,PATCH,PUT,DELETE'` | CORS allowed methods |
77
77
  | `enableHealthCheck` | **Boolean** | `true` | Register `GET /health` endpoint (disable via `REST_HEALTH_CHECK_DISABLE=true`) |
78
+ | `trustProxy` | **Boolean** | `false` | Trust reverse proxy headers (e.g. `X-Forwarded-Proto`). Enable via `REST_TRUST_PROXY=true` when running behind a load balancer such as AWS ALB/ELB to ensure correct protocol detection. |
78
79
  | `statusMap` | **Object** | `{}` | Optional mapping of HTTP status codes to custom messages |
79
80
 
81
+ ### Running Behind a Load Balancer
82
+
83
+ When your application runs behind a reverse proxy or load balancer (e.g. AWS ALB/ELB), the load balancer terminates SSL and forwards requests to your server over HTTP internally. This means Express sees `http` as the protocol even though the original client request used `https`.
84
+
85
+ To fix this, enable the `trustProxy` option:
86
+
87
+ ```bash
88
+ REST_TRUST_PROXY=true
89
+ ```
90
+
91
+ This tells Express to trust the `X-Forwarded-Proto` header set by the load balancer, so `request.protocol` correctly returns `https`. This is important for any functionality that generates URLs based on the incoming request protocol, such as JSON:API relationship links.
92
+
80
93
  ## Request Class
81
94
 
82
95
  The `Request` class provides a structured way to define route handlers and authorization hooks. Route classes extend `Request` and define:
@@ -3,7 +3,8 @@ const {
3
3
  REST_CORS_METHODS,
4
4
  REST_HEALTH_CHECK_DISABLE,
5
5
  REST_PORT,
6
- REST_REQUEST_PATH
6
+ REST_REQUEST_PATH,
7
+ REST_TRUST_PROXY
7
8
  } = process;
8
9
 
9
10
  export default {
@@ -12,6 +13,7 @@ export default {
12
13
  methods: REST_CORS_METHODS ?? 'GET,POST,PATCH,PUT,DELETE',
13
14
  dir: REST_REQUEST_PATH ?? './requests',
14
15
  port: REST_PORT ?? 2666,
16
+ trustProxy: REST_TRUST_PROXY === 'true',
15
17
  logColor: 'yellow',
16
18
  logMethod: 'api'
17
19
  };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "stonyx-async",
5
5
  "stonyx-module"
6
6
  ],
7
- "version": "0.2.1-alpha.3",
7
+ "version": "0.2.1-alpha.4",
8
8
  "description": "Rest Server Module for Stonyx Framework",
9
9
  "repository": {
10
10
  "type": "git",
package/src/main.js CHANGED
@@ -61,7 +61,9 @@ export default class RestServer {
61
61
  }
62
62
 
63
63
  async setupGlobalMiddleware() {
64
- const { origin, methods } = config.restServer;
64
+ const { origin, methods, trustProxy } = config.restServer;
65
+
66
+ if (trustProxy) this.api.set('trust proxy', true);
65
67
 
66
68
  this.api.use([
67
69
  cors({ origin, methods }),
@@ -73,17 +75,12 @@ export default class RestServer {
73
75
  const { api } = this;
74
76
  const classInstance = new routeClass(options);
75
77
  const route = name === 'index' ? '/' : `/${name}`;
76
- const { expressInstance, authorization } = classInstance;
77
-
78
- const routeCalls = [ expressInstance ];
79
-
80
- // Assign auth callback if it exists
81
- if (authorization) routeCalls.unshift(authorization.bind(classInstance));
78
+ const { expressInstance } = classInstance;
82
79
 
83
80
  classInstance.registerCalls();
84
81
  expressInstance.mountpath = route;
85
-
82
+
86
83
  // Mount handler to main api instance
87
- api.use(route, ...routeCalls);
84
+ api.use(route, expressInstance);
88
85
  }
89
86
  }
package/src/request.js CHANGED
@@ -29,20 +29,10 @@ export default class Request {
29
29
  this.expressInstance = api;
30
30
  }
31
31
 
32
- // auth hook wrapper
33
- authorization(req, res, next) {
34
- if (!this.auth) return next();
35
-
36
- const status = this.auth(req, Request.getState(req));
37
- if (status) return Request.sendStatusResponse(res, status);
38
-
39
- next();
40
- }
41
-
42
32
  registerCalls() {
43
33
  const { expressInstance } = this;
44
- const { getState } = Request;
45
-
34
+ const { getState, sendStatusResponse } = Request;
35
+
46
36
  for (const [method, handlers] of Object.entries(this.handlers)) {
47
37
  if (!METHODS.has(method)) {
48
38
  console.warn(`Method "${method}" is not a valid HTTP method. Skipping...`);
@@ -51,9 +41,14 @@ export default class Request {
51
41
 
52
42
  for (const [route, handler] of Object.entries(handlers)) {
53
43
  expressInstance[method](route, async (req, res) => {
44
+ // Run auth after route matching so request.params is populated
45
+ if (this.auth) {
46
+ const status = this.auth(req, getState(req));
47
+ if (status) return sendStatusResponse(res, status);
48
+ }
49
+
54
50
  const callStack = [...makeArray(handler)];
55
51
  const mainCall = callStack.pop();
56
- const { sendStatusResponse } = Request;
57
52
  let response;
58
53
 
59
54
  // Run middleware
@@ -64,7 +59,7 @@ export default class Request {
64
59
 
65
60
  if (response === undefined) response = await mainCall(req, getState(req));
66
61
  if (Number.isInteger(response)) return sendStatusResponse(res, response);
67
-
62
+
68
63
  // Handle redirect if set via call state object
69
64
  const { redirect } = getState(req);
70
65
  if (redirect) return res.redirect(redirect);