@stonyx/rest-server 0.2.1-alpha.0 → 0.2.1-alpha.2
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.
- package/.claude/improvements.md +44 -0
- package/.claude/project-structure.md +150 -0
- package/.github/workflows/ci.yml +5 -25
- package/.github/workflows/publish.yml +24 -116
- package/README.md +21 -14
- package/config/environment.js +3 -1
- package/package.json +4 -4
- package/src/main.js +3 -1
- package/stonyx-bootstrap.cjs +0 -12
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Improvement Opportunities
|
|
2
|
+
|
|
3
|
+
## 1. `new express()` in RestServer constructor
|
|
4
|
+
|
|
5
|
+
**File**: `src/main.js`, line 30
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
this.api = new express();
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Express 5 (which this project targets via `"express": "^5.1.0"`) documents calling `express()` as a plain function, not as a constructor with `new`. While `new express()` works in practice because the function returns a new object regardless, it is unconventional and may break if Express ever enforces non-constructor semantics. Consider changing to:
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
this.api = express();
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Note: The `Request` class in `src/request.js` line 26 already uses the correct pattern (`const api = express();` without `new`).
|
|
18
|
+
|
|
19
|
+
## 2. `setupGlobalMiddleware` is `async` but contains no awaits
|
|
20
|
+
|
|
21
|
+
**File**: `src/main.js`, lines 63-70
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
async setupGlobalMiddleware() {
|
|
25
|
+
const { origin, methods } = config.restServer;
|
|
26
|
+
|
|
27
|
+
this.api.use([
|
|
28
|
+
cors({ origin, methods }),
|
|
29
|
+
express.json()
|
|
30
|
+
]);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The `async` keyword is unnecessary here since the method body contains no `await` expressions and `app.use()` is synchronous. The `async` keyword causes the method to return a `Promise` wrapping `undefined`, but the caller (`setupRouter`) does not `await` it either — it calls `this.setupGlobalMiddleware()` without `await` on line 52. Removing `async` would make the intent clearer.
|
|
35
|
+
|
|
36
|
+
## 3. `logMethod` config option is not used in rest-server source
|
|
37
|
+
|
|
38
|
+
**File**: `config/environment.js`, line 16
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
logMethod: 'api'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The `logMethod` property is defined in the default config but is never referenced anywhere in `src/main.js` or `src/request.js`. It is likely consumed by the Stonyx framework core (`stonyx/log`) for registering a named log method, but this should be verified. If it is indeed framework-level plumbing, consider documenting that it is a Stonyx convention rather than a rest-server feature. If it is unused, consider removing it to reduce config surface area.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# @stonyx/rest-server — Project Structure
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
REST server module for the Stonyx framework. Provides dynamic route registration from a file directory, built-in CORS/JSON middleware, per-route authorization hooks, and a structured `Request` base class for defining handlers.
|
|
6
|
+
|
|
7
|
+
- **Package**: `@stonyx/rest-server` (v0.2.1-beta.1)
|
|
8
|
+
- **License**: Apache-2.0
|
|
9
|
+
- **Entry point**: `src/main.js`
|
|
10
|
+
- **Module type**: ESM (`"type": "module"`)
|
|
11
|
+
- **Node version**: v24.13.0 (per `.nvmrc`)
|
|
12
|
+
- **Package manager**: pnpm
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
### RestServer (src/main.js)
|
|
17
|
+
|
|
18
|
+
Singleton class wrapping an Express 5 instance.
|
|
19
|
+
|
|
20
|
+
- **Constructor** — enforces singleton via `RestServer.instance`; creates the Express app with `new express()`
|
|
21
|
+
- **`init()`** — calls `setupRouter()`, then starts listening on the configured port
|
|
22
|
+
- **`setupRouter()`** — calls `setupGlobalMiddleware()`, then uses `forEachFileImport` (from `@stonyx/utils/file`) to dynamically import all files in the configured `dir` and mount each as a route via `mountRoute()`. Optionally registers a `/health` endpoint.
|
|
23
|
+
- **`setupGlobalMiddleware()`** — attaches `cors()` and `express.json()` middleware to the Express app
|
|
24
|
+
- **`mountRoute(routeClass, { name, options })`** — instantiates the imported Request subclass, wires up the `authorization` middleware if present, calls `registerCalls()`, and mounts the sub-app at `/<filename>`
|
|
25
|
+
- **`RestServer.close()`** — static method to close the server
|
|
26
|
+
|
|
27
|
+
### Request (src/request.js)
|
|
28
|
+
|
|
29
|
+
Base class for route definitions. Each file in the requests directory exports a class extending `Request`.
|
|
30
|
+
|
|
31
|
+
- **Constructor** — creates a child Express instance with `x-powered-by` disabled
|
|
32
|
+
- **`handlers`** — instance property: object mapping HTTP methods (`get`, `post`, `put`, `delete`, `patch`) to route-path/handler pairs
|
|
33
|
+
- **`auth(req, state)`** — optional hook. Return an integer status code to reject the request; return nothing to allow it through.
|
|
34
|
+
- **`authorization(req, res, next)`** — wrapper that calls `auth()` and short-circuits with a status response if it returns a code
|
|
35
|
+
- **`registerCalls()`** — iterates `handlers`, registers each route on the child Express instance. Supports:
|
|
36
|
+
- Single handler function or array (last element is the main handler, preceding elements are middleware)
|
|
37
|
+
- Middleware functions are bound to the class instance and executed in order
|
|
38
|
+
- Integer return = status code response (via `sendStatusResponse`)
|
|
39
|
+
- Object return = JSON response
|
|
40
|
+
- `undefined` return = 200 OK
|
|
41
|
+
- Pipe support via `state.pipe` (sets headers and pipes a stream)
|
|
42
|
+
- **`Request.getState(req)`** — attaches/retrieves a `__stonyxState` object on the Express request
|
|
43
|
+
- **`Request.sendStatusResponse(res, status)`** — sends status with optional custom message from `config.restServer.statusMap`
|
|
44
|
+
|
|
45
|
+
Valid HTTP methods (enforced): `get`, `post`, `put`, `delete`, `patch`
|
|
46
|
+
|
|
47
|
+
## Configuration Reference
|
|
48
|
+
|
|
49
|
+
From `config/environment.js`. All values are overridable via environment variables.
|
|
50
|
+
|
|
51
|
+
| Option | Type | Default | Env Var | Description |
|
|
52
|
+
|---------------------|-------------------|-------------------------------|----------------------------|-----------------------------------------------------------------|
|
|
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 |
|
|
55
|
+
| `origin` | **String** | `'*'` | `REST_CORS_ORIGIN` | CORS allowed origin(s) |
|
|
56
|
+
| `methods` | **String** | `'GET,POST,PATCH,PUT,DELETE'` | `REST_CORS_METHODS` | CORS allowed methods |
|
|
57
|
+
| `dir` | **String** | `'./requests'` | `REST_REQUEST_PATH` | Directory containing Request class files to mount as routes |
|
|
58
|
+
| `port` | **Number/String** | `2666` | `REST_PORT` | Port the REST server listens on |
|
|
59
|
+
| `logColor` | **String** | `'yellow'` | — | Console log color for this module (Stonyx logging integration) |
|
|
60
|
+
| `logMethod` | **String** | `'api'` | — | Log method name (Stonyx logging integration) |
|
|
61
|
+
|
|
62
|
+
Additional config used (not rest-server-specific):
|
|
63
|
+
- `config.debug` (top-level Stonyx config) — if truthy, logs errors during route setup
|
|
64
|
+
- `config.restServer.statusMap` (optional, no default in environment.js) — maps status codes to custom message strings
|
|
65
|
+
- `config.restServer.camelCaseRoutes` (optional, no default in environment.js) — when falsy, passes `rawName: true` to `forEachFileImport` so filenames are used as-is for route paths
|
|
66
|
+
|
|
67
|
+
## Test Structure
|
|
68
|
+
|
|
69
|
+
Tests use **QUnit** and run via `stonyx test` (the `npm test` script).
|
|
70
|
+
|
|
71
|
+
### test/config/environment.js
|
|
72
|
+
Overrides `restServer.dir` to `'./test/sample/requests'` so tests load sample request classes.
|
|
73
|
+
|
|
74
|
+
### test/unit/request-test.js
|
|
75
|
+
Unit tests for `Request` static methods:
|
|
76
|
+
- `getState` — creates/returns state object on request
|
|
77
|
+
- `sendStatusResponse` — sends status with optional `statusMap` message
|
|
78
|
+
|
|
79
|
+
### test/integration/rest-server-test.js
|
|
80
|
+
Integration tests that boot the full server and make HTTP requests:
|
|
81
|
+
- 404 for non-existent routes
|
|
82
|
+
- `/public` — JSON response, 200 OK default, URL params, middleware (success/failure), `this` binding for handlers and middleware
|
|
83
|
+
- `/private` — authenticated success, auth hook rejection (505)
|
|
84
|
+
- `/health` — health check endpoint returns 200
|
|
85
|
+
|
|
86
|
+
### test/sample/requests/
|
|
87
|
+
Sample Request subclasses used by integration tests:
|
|
88
|
+
- `public.js` — `PublicRequest` with various GET handlers demonstrating middleware, params, binding
|
|
89
|
+
- `private.js` — `PrivateRequest` with `auth()` hook that rejects `/failure` with 505
|
|
90
|
+
|
|
91
|
+
## CI/CD
|
|
92
|
+
|
|
93
|
+
### .github/workflows/ci.yml
|
|
94
|
+
Runs on pull requests to `dev` and `main`. Delegates to shared workflow at `abofs/stonyx-workflows`.
|
|
95
|
+
|
|
96
|
+
### .github/workflows/publish.yml
|
|
97
|
+
Publishes to NPM. Triggered by:
|
|
98
|
+
- `workflow_dispatch` with version-type selection (patch/minor/major) or custom version
|
|
99
|
+
- Pull requests to `main`/`dev`
|
|
100
|
+
- Pushes to `main`
|
|
101
|
+
|
|
102
|
+
Delegates to `abofs/stonyx-workflows/.github/workflows/npm-publish.yml@main`.
|
|
103
|
+
|
|
104
|
+
## File Structure
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
stonyx-rest-server/
|
|
108
|
+
├── .claude/
|
|
109
|
+
│ ├── improvements.md
|
|
110
|
+
│ └── project-structure.md # this file
|
|
111
|
+
├── .github/
|
|
112
|
+
│ └── workflows/
|
|
113
|
+
│ ├── ci.yml # PR CI — delegates to shared workflow
|
|
114
|
+
│ └── publish.yml # NPM publish — delegates to shared workflow
|
|
115
|
+
├── config/
|
|
116
|
+
│ └── environment.js # Default config with env var overrides
|
|
117
|
+
├── src/
|
|
118
|
+
│ ├── main.js # RestServer class (singleton, Express wrapper)
|
|
119
|
+
│ └── request.js # Request base class (handler registration, auth hook)
|
|
120
|
+
├── test/
|
|
121
|
+
│ ├── config/
|
|
122
|
+
│ │ └── environment.js # Test config override (dir → test/sample/requests)
|
|
123
|
+
│ ├── integration/
|
|
124
|
+
│ │ └── rest-server-test.js # Integration tests (QUnit)
|
|
125
|
+
│ ├── sample/
|
|
126
|
+
│ │ └── requests/
|
|
127
|
+
│ │ ├── private.js # Sample private request with auth hook
|
|
128
|
+
│ │ └── public.js # Sample public request with middleware demos
|
|
129
|
+
│ └── unit/
|
|
130
|
+
│ └── request-test.js # Unit tests for Request statics (QUnit)
|
|
131
|
+
├── .gitignore
|
|
132
|
+
├── .npmignore
|
|
133
|
+
├── .nvmrc # Node v24.13.0
|
|
134
|
+
├── LICENSE.md # Apache 2.0
|
|
135
|
+
├── package.json
|
|
136
|
+
├── pnpm-lock.yaml
|
|
137
|
+
└── README.md
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Dependencies
|
|
141
|
+
|
|
142
|
+
### Runtime
|
|
143
|
+
- `cors` ^2.8.5 — CORS middleware
|
|
144
|
+
- `express` ^5.1.0 — HTTP framework
|
|
145
|
+
- `stonyx` (local link) — Framework core (config, logging)
|
|
146
|
+
|
|
147
|
+
### Dev
|
|
148
|
+
- `@stonyx/utils` (local link) — Utility functions (file import, object helpers)
|
|
149
|
+
- `qunit` ^2.24.1 — Test framework
|
|
150
|
+
- `sinon` ^21.0.0 — Test stubs/spies
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -2,35 +2,15 @@ name: CI
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
pull_request:
|
|
5
|
-
branches:
|
|
6
|
-
- dev
|
|
7
|
-
- main
|
|
5
|
+
branches: [dev, main]
|
|
8
6
|
|
|
9
7
|
concurrency:
|
|
10
8
|
group: ci-${{ github.head_ref || github.ref }}
|
|
11
9
|
cancel-in-progress: true
|
|
12
10
|
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
13
14
|
jobs:
|
|
14
15
|
test:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
steps:
|
|
18
|
-
- name: Checkout code
|
|
19
|
-
uses: actions/checkout@v3
|
|
20
|
-
|
|
21
|
-
- name: Setup pnpm
|
|
22
|
-
uses: pnpm/action-setup@v4
|
|
23
|
-
with:
|
|
24
|
-
version: 9
|
|
25
|
-
|
|
26
|
-
- name: Set up Node.js
|
|
27
|
-
uses: actions/setup-node@v3
|
|
28
|
-
with:
|
|
29
|
-
node-version: 24.13.0
|
|
30
|
-
cache: 'pnpm'
|
|
31
|
-
|
|
32
|
-
- name: Install dependencies
|
|
33
|
-
run: pnpm install --frozen-lockfile
|
|
34
|
-
|
|
35
|
-
- name: Run tests
|
|
36
|
-
run: pnpm test
|
|
16
|
+
uses: abofs/stonyx-workflows/.github/workflows/ci.yml@main
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
name: Publish to NPM
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
|
|
4
|
+
repository_dispatch:
|
|
5
|
+
types: [cascade-publish]
|
|
5
6
|
workflow_dispatch:
|
|
6
7
|
inputs:
|
|
7
8
|
version-type:
|
|
@@ -9,7 +10,6 @@ on:
|
|
|
9
10
|
required: true
|
|
10
11
|
type: choice
|
|
11
12
|
options:
|
|
12
|
-
- alpha
|
|
13
13
|
- patch
|
|
14
14
|
- minor
|
|
15
15
|
- major
|
|
@@ -17,127 +17,35 @@ on:
|
|
|
17
17
|
description: 'Custom version (optional, overrides version-type)'
|
|
18
18
|
required: false
|
|
19
19
|
type: string
|
|
20
|
-
|
|
21
|
-
# Auto-publish alpha on PR
|
|
22
20
|
pull_request:
|
|
23
21
|
types: [opened, synchronize, reopened]
|
|
24
|
-
branches: [main
|
|
25
|
-
|
|
26
|
-
# Auto-publish stable on merge to main
|
|
22
|
+
branches: [main]
|
|
27
23
|
push:
|
|
28
24
|
branches: [main]
|
|
29
25
|
|
|
26
|
+
concurrency:
|
|
27
|
+
group: ${{ github.event_name == 'repository_dispatch' && 'cascade-update' || format('publish-{0}', github.ref) }}
|
|
28
|
+
cancel-in-progress: false
|
|
29
|
+
|
|
30
30
|
permissions:
|
|
31
31
|
contents: write
|
|
32
|
-
id-token: write
|
|
33
|
-
pull-requests: write
|
|
32
|
+
id-token: write
|
|
33
|
+
pull-requests: write
|
|
34
34
|
|
|
35
35
|
jobs:
|
|
36
36
|
publish:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
- name: Set up Node.js
|
|
53
|
-
uses: actions/setup-node@v3
|
|
54
|
-
with:
|
|
55
|
-
node-version: 24.13.0
|
|
56
|
-
cache: 'pnpm'
|
|
57
|
-
registry-url: 'https://registry.npmjs.org'
|
|
58
|
-
|
|
59
|
-
- name: Install dependencies
|
|
60
|
-
run: pnpm install --frozen-lockfile
|
|
61
|
-
|
|
62
|
-
- name: Run tests
|
|
63
|
-
run: pnpm test
|
|
64
|
-
|
|
65
|
-
- name: Configure git
|
|
66
|
-
run: |
|
|
67
|
-
git config user.name "github-actions[bot]"
|
|
68
|
-
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
69
|
-
|
|
70
|
-
# Determine version type based on trigger
|
|
71
|
-
- name: Determine version bump type
|
|
72
|
-
id: version-type
|
|
73
|
-
run: |
|
|
74
|
-
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
75
|
-
echo "type=alpha" >> $GITHUB_OUTPUT
|
|
76
|
-
elif [ "${{ github.event_name }}" = "push" ]; then
|
|
77
|
-
echo "type=patch" >> $GITHUB_OUTPUT
|
|
78
|
-
elif [ "${{ github.event.inputs.custom-version }}" != "" ]; then
|
|
79
|
-
echo "type=custom" >> $GITHUB_OUTPUT
|
|
80
|
-
else
|
|
81
|
-
echo "type=${{ github.event.inputs.version-type }}" >> $GITHUB_OUTPUT
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
# Version bumping
|
|
85
|
-
- name: Bump version (custom)
|
|
86
|
-
if: steps.version-type.outputs.type == 'custom'
|
|
87
|
-
run: pnpm version ${{ github.event.inputs.custom-version }} --no-git-tag-version
|
|
88
|
-
|
|
89
|
-
- name: Bump version (alpha)
|
|
90
|
-
if: steps.version-type.outputs.type == 'alpha'
|
|
91
|
-
run: pnpm version prerelease --preid=alpha --no-git-tag-version
|
|
92
|
-
|
|
93
|
-
- name: Bump version (patch/minor/major)
|
|
94
|
-
if: steps.version-type.outputs.type == 'patch' || steps.version-type.outputs.type == 'minor' || steps.version-type.outputs.type == 'major'
|
|
95
|
-
run: pnpm version ${{ steps.version-type.outputs.type }} --no-git-tag-version
|
|
96
|
-
|
|
97
|
-
- name: Get package version
|
|
98
|
-
id: package-version
|
|
99
|
-
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
|
100
|
-
|
|
101
|
-
# Publishing
|
|
102
|
-
- name: Publish to NPM (alpha)
|
|
103
|
-
if: contains(steps.package-version.outputs.version, 'alpha')
|
|
104
|
-
run: pnpm publish --tag alpha --access public --no-git-checks
|
|
105
|
-
|
|
106
|
-
- name: Publish to NPM (stable)
|
|
107
|
-
if: "!contains(steps.package-version.outputs.version, 'alpha')"
|
|
108
|
-
run: pnpm publish --access public
|
|
109
|
-
|
|
110
|
-
# Only commit and tag for stable releases (push to main or manual stable)
|
|
111
|
-
- name: Commit version bump and create tag
|
|
112
|
-
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && !contains(steps.package-version.outputs.version, 'alpha'))
|
|
113
|
-
run: |
|
|
114
|
-
git add package.json
|
|
115
|
-
git commit -m "chore: release v${{ steps.package-version.outputs.version }}"
|
|
116
|
-
git tag v${{ steps.package-version.outputs.version }}
|
|
117
|
-
git push origin main --tags
|
|
118
|
-
|
|
119
|
-
- name: Create GitHub Release
|
|
120
|
-
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && !contains(steps.package-version.outputs.version, 'alpha'))
|
|
121
|
-
uses: actions/create-release@v1
|
|
122
|
-
env:
|
|
123
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
124
|
-
with:
|
|
125
|
-
tag_name: v${{ steps.package-version.outputs.version }}
|
|
126
|
-
release_name: v${{ steps.package-version.outputs.version }}
|
|
127
|
-
draft: false
|
|
128
|
-
prerelease: false
|
|
129
|
-
|
|
130
|
-
# Add PR comment with alpha version info
|
|
131
|
-
- name: Comment on PR with alpha version
|
|
132
|
-
if: github.event_name == 'pull_request'
|
|
133
|
-
uses: actions/github-script@v6
|
|
134
|
-
with:
|
|
135
|
-
script: |
|
|
136
|
-
const version = '${{ steps.package-version.outputs.version }}';
|
|
137
|
-
const packageName = require('./package.json').name;
|
|
138
|
-
github.rest.issues.createComment({
|
|
139
|
-
issue_number: context.issue.number,
|
|
140
|
-
owner: context.repo.owner,
|
|
141
|
-
repo: context.repo.repo,
|
|
142
|
-
body: `## 🚀 Alpha Version Published\n\n**Version:** \`${version}\`\n\n**Install:**\n\`\`\`bash\npnpm add ${packageName}@${version}\n# or\npnpm add ${packageName}@alpha # latest alpha\n\`\`\`\n\nThis alpha version is now available for testing!`
|
|
143
|
-
});
|
|
37
|
+
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
|
38
|
+
uses: abofs/stonyx-workflows/.github/workflows/npm-publish.yml@main
|
|
39
|
+
with:
|
|
40
|
+
version-type: ${{ github.event.inputs.version-type }}
|
|
41
|
+
custom-version: ${{ github.event.inputs.custom-version }}
|
|
42
|
+
cascade-source: ${{ github.event.client_payload.source_package || '' }}
|
|
43
|
+
secrets: inherit
|
|
44
|
+
|
|
45
|
+
cascade:
|
|
46
|
+
needs: publish
|
|
47
|
+
uses: abofs/stonyx-workflows/.github/workflows/cascade.yml@main
|
|
48
|
+
with:
|
|
49
|
+
package-name: ${{ needs.publish.outputs.package-name }}
|
|
50
|
+
published-version: ${{ needs.publish.outputs.published-version }}
|
|
51
|
+
secrets: inherit
|
package/README.md
CHANGED
|
@@ -41,16 +41,13 @@ export default {
|
|
|
41
41
|
};
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Then
|
|
44
|
+
Then run the application via the Stonyx CLI, which auto-initializes all modules including the REST server:
|
|
45
45
|
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
import config from './config/environment.js';
|
|
49
|
-
|
|
50
|
-
new Stonyx(config);
|
|
46
|
+
```bash
|
|
47
|
+
stonyx serve
|
|
51
48
|
```
|
|
52
49
|
|
|
53
|
-
For further framework
|
|
50
|
+
For further framework instructions, see the [Stonyx repository](https://github.com/abofs/stonyx).
|
|
54
51
|
|
|
55
52
|
### Optional Direct Usage
|
|
56
53
|
|
|
@@ -72,12 +69,26 @@ Configuration is read from `stonyx/config` under `restServer`:
|
|
|
72
69
|
|
|
73
70
|
| Option | Type | Default | Description |
|
|
74
71
|
| :---------------: | :-----------------: | :---------- | :--------------------------------------------------------- |
|
|
75
|
-
| `dir` | **String** | `
|
|
72
|
+
| `dir` | **String** | `'./requests'` | Directory containing request classes to mount as routes |
|
|
76
73
|
| `camelCaseRoutes` | **Boolean** | `true` | Convert filenames to camelCase when generating route paths |
|
|
77
|
-
| `port` | **Number** | `
|
|
74
|
+
| `port` | **Number** | `2666` | Port to listen on |
|
|
78
75
|
| `origin` | **String \| Array** | `'*'` | CORS origin(s) allowed |
|
|
76
|
+
| `methods` | **String** | `'GET,POST,PATCH,PUT,DELETE'` | CORS allowed methods |
|
|
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. |
|
|
79
79
|
| `statusMap` | **Object** | `{}` | Optional mapping of HTTP status codes to custom messages |
|
|
80
|
-
|
|
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.
|
|
81
92
|
|
|
82
93
|
## Request Class
|
|
83
94
|
|
|
@@ -135,10 +146,6 @@ project-root/
|
|
|
135
146
|
|
|
136
147
|
The `RestServer` will automatically mount these routes using the filenames as paths (`/public` and `/private` by default, or camelCased if configured).
|
|
137
148
|
|
|
138
|
-
Perfect! Here’s a self-contained **“Example Requests”** section with sample `curl` calls:
|
|
139
|
-
|
|
140
|
-
---
|
|
141
|
-
|
|
142
149
|
### Example Requests
|
|
143
150
|
|
|
144
151
|
Assuming you have `public.js` and `private.js` routes mounted, you can test them like this:
|
package/config/environment.js
CHANGED
|
@@ -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.
|
|
7
|
+
"version": "0.2.1-alpha.2",
|
|
8
8
|
"description": "Rest Server Module for Stonyx Framework",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"cors": "^2.8.5",
|
|
29
29
|
"express": "^5.1.0",
|
|
30
|
-
"stonyx": "
|
|
30
|
+
"stonyx": "0.2.3-beta.2"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@stonyx/utils": "
|
|
33
|
+
"@stonyx/utils": "0.2.3-beta.3",
|
|
34
34
|
"qunit": "^2.24.1",
|
|
35
35
|
"sinon": "^21.0.0"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
|
-
"test": "
|
|
38
|
+
"test": "stonyx test"
|
|
39
39
|
}
|
|
40
40
|
}
|
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 }),
|
package/stonyx-bootstrap.cjs
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* commonJS Bootstrap loading - Stonyx must be loaded first, prior to the rest of the application
|
|
3
|
-
*/
|
|
4
|
-
const { default:Stonyx } = require('stonyx');
|
|
5
|
-
const { default:config } = require('./config/environment.js');
|
|
6
|
-
|
|
7
|
-
// Override dir for tests
|
|
8
|
-
config.dir = './test/sample/requests';
|
|
9
|
-
|
|
10
|
-
new Stonyx(config, __dirname);
|
|
11
|
-
|
|
12
|
-
module.exports = Stonyx;
|