@lenne.tech/nest-server 11.42.5 → 11.42.6
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/rules/configurable-features.md +1 -1
- package/.claude/rules/testing.md +3 -3
- package/FRAMEWORK-API.md +1 -1
- package/dist/core/common/services/core-body-parser.initializer.d.ts +2 -0
- package/dist/core/common/services/core-body-parser.initializer.js +23 -3
- package/dist/core/common/services/core-body-parser.initializer.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/docs/REQUEST-LIFECYCLE.md +1 -1
- package/migration-guides/11.42.3-to-11.42.4.md +10 -1
- package/migration-guides/11.42.5-to-11.42.6.md +97 -0
- package/package.json +1 -1
- package/src/core/common/interfaces/server-options.interface.ts +5 -0
- package/src/core/common/services/core-body-parser.initializer.ts +47 -5
|
@@ -569,7 +569,7 @@ Both use body-parser's default limit of **100 kB** unless `bodyParser` says othe
|
|
|
569
569
|
bodyParser: { json: { limit: '2mb' } },
|
|
570
570
|
```
|
|
571
571
|
|
|
572
|
-
`CoreBodyParserInitializer` applies it during module init by replacing the handle of the global parser layers NestJS created — in place, because NestJS registers them before any init hook runs and a parser appended later would sit behind the routes. Running during module init is what makes the limit hold under `Test.createTestingModule()`, which never runs `main.ts`. A path-scoped parser (`server.use('/upload', json({ limit }))` in `main.ts`) runs before the global one and is left untouched — that remains the way to give one path a different limit.
|
|
572
|
+
`CoreBodyParserInitializer` applies it during module init by replacing the handle of the global parser layers NestJS created — in place, because NestJS registers them before any init hook runs and a parser appended later would sit behind the routes. Running during module init is what makes the limit hold under `Test.createTestingModule()`, which never runs `main.ts`. A path-scoped parser (`server.use('/upload', json({ limit }))` in `main.ts`) runs before the global one and is left untouched — that remains the way to give one path a different limit. Keep `server.useBodyParser('json')` (or `'urlencoded'`) in `main.ts` next to it: NestJS registers its global parser only when no layer with the same function name exists, so a path-scoped one registered before `app.init()` suppresses it, and every other path receives an empty body. Since 11.42.6 `CoreBodyParserInitializer` warns at boot in that case.
|
|
573
573
|
|
|
574
574
|
The parser runs **before authentication and routing**. Over the limit, the whole request is refused:
|
|
575
575
|
|
|
@@ -63,6 +63,14 @@ server.useBodyParser('json', { limit: '2mb' });
|
|
|
63
63
|
bodyParser: { json: { limit: '2mb' } },
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
**Exception: keep `useBodyParser()` when `main.ts` also registers a path-scoped parser** such as
|
|
67
|
+
`server.use('/upload', json({ limit: '20mb' }))`. NestJS registers its own global parser only if no
|
|
68
|
+
layer with the same function name exists yet, and a path-scoped `json()` registered before
|
|
69
|
+
`app.init()` counts as one. Removing `useBodyParser()` then leaves the app without a global JSON
|
|
70
|
+
parser: every request outside that path reaches its handler with an empty body, and a sign-in fails
|
|
71
|
+
with a validation error. Keep `server.useBodyParser('json')` (the configured limit is applied to it)
|
|
72
|
+
and set the limit in the config as above. Since 11.42.6 the server warns at boot when it finds this.
|
|
73
|
+
|
|
66
74
|
**Starter-based projects:** `config.env.ts` builds `local` / `e2e` / `ci` from `localConfig()` and the
|
|
67
75
|
deployed environments from `deployedConfig()`. Put the limit where BOTH see it — the starter ships a
|
|
68
76
|
shared `PROJECT_BODY_PARSER` constant for this since its 11.42.4 update — or set
|
|
@@ -75,7 +83,8 @@ The config is applied during module init, so it holds in every test that builds
|
|
|
75
83
|
global parser your `main.ts` registered; keeping both is harmless, but the config wins.
|
|
76
84
|
|
|
77
85
|
A **path-scoped** parser is left alone: `server.use('/upload', json({ limit: '20mb' }))` in `main.ts`
|
|
78
|
-
still gives that one path its own limit
|
|
86
|
+
still gives that one path its own limit, as long as the global parser stays registered next to it
|
|
87
|
+
(see the exception above). The parser runs before routing, so a per-route limit can only
|
|
79
88
|
key on the path — and all GraphQL operations share one.
|
|
80
89
|
|
|
81
90
|
**`rawBody: true`:** `req.rawBody` is kept for apps created with `NestFactory.create(…, { rawBody:
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Migration Guide: 11.42.5 → 11.42.6
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
| Category | Details |
|
|
6
|
+
| -------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
7
|
+
| **Correction** | The 11.42.3 → 11.42.4 guide told every project to drop `useBodyParser()` from `main.ts`. With a path-scoped parser in `main.ts` that removes JSON parsing everywhere else (§1) |
|
|
8
|
+
| **Improvement** | The server warns at boot when NestJS skipped its global body parser because of such a path-scoped one (§1) |
|
|
9
|
+
| **Tooling** | `check:overrides` no longer reports a failed audit as clean, and its probe follows `pnpm_config_registry` (§2) |
|
|
10
|
+
| **Breaking Changes** | None |
|
|
11
|
+
| **Migration Effort** | None for most projects. Projects that removed `useBodyParser()` while registering a path-scoped parser in `main.ts` must add it back (§1) |
|
|
12
|
+
|
|
13
|
+
## Quick Migration
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm update @lenne.tech/nest-server
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then check `main.ts` as described in §1. Vendor-mode projects pick up the core update and need the
|
|
20
|
+
same check.
|
|
21
|
+
|
|
22
|
+
## 1. Keep `useBodyParser()` next to a path-scoped parser
|
|
23
|
+
|
|
24
|
+
§1 of the [11.42.3 → 11.42.4 guide](./11.42.3-to-11.42.4.md) recommended moving the body limit from
|
|
25
|
+
`main.ts` into `bodyParser.json.limit` and deleting the `useBodyParser()` call. That is right, with one
|
|
26
|
+
exception the guide did not name.
|
|
27
|
+
|
|
28
|
+
NestJS registers its own global JSON and URL-encoded parsers inside `app.init()`, but only when no
|
|
29
|
+
layer with the same function name exists yet. It compares the NAME and nothing else. A path-scoped
|
|
30
|
+
parser registered in `main.ts` before `listen()` carries that name:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// main.ts
|
|
34
|
+
server.use('/diagnostics', json({ limit: '256kb' })); // a layer named `jsonParser`
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
With `useBodyParser()` removed, NestJS treats the JSON parser as already applied and registers none.
|
|
38
|
+
Every request outside `/diagnostics` then reaches its handler with an empty body. A sign-in fails with
|
|
39
|
+
a validation error such as "Missing input", and before this release nothing was logged.
|
|
40
|
+
|
|
41
|
+
**Check `main.ts`.** If it registers `json(...)` or `urlencoded(...)` on a path, keep or restore the
|
|
42
|
+
global parser next to it:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// main.ts
|
|
46
|
+
server.use('/diagnostics', json({ limit: '256kb' }));
|
|
47
|
+
server.useBodyParser('json'); // the configured bodyParser.json.limit is applied to it
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Keep the limit itself in the config (`bodyParser.json.limit`), where it also holds under
|
|
51
|
+
`Test.createTestingModule()`. The e2e suite cannot catch the missing parser: the testing module never
|
|
52
|
+
runs `main.ts`, so it never registers the path-scoped parser either.
|
|
53
|
+
|
|
54
|
+
Since 11.42.6 the server logs a warning at boot in exactly this situation:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
No global json body parser is registered: NestJS skips its own when a layer named `jsonParser`
|
|
58
|
+
already exists, and a path-scoped `json()` parser registered before `app.init()` counts as one. …
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The framework only reports it. It does not register the missing parser itself: NestJS would have put
|
|
62
|
+
it after everything `main.ts` registered, and that position is no longer known during module init. A
|
|
63
|
+
raw-body webhook or a proxy registered after the path-scoped parser must keep receiving the unread
|
|
64
|
+
request stream.
|
|
65
|
+
|
|
66
|
+
## 2. `check:overrides`: a failed audit is no longer reported as clean
|
|
67
|
+
|
|
68
|
+
Two fixes in `scripts/check-overrides.mjs` (and the shared registry block in `scripts/check.mjs`):
|
|
69
|
+
|
|
70
|
+
- When pnpm 11 cannot reach the registry, `pnpm audit --json` prints
|
|
71
|
+
`{"error": {"message": "fetch failed"}}` instead of a report. The guard read that as "0 advisories"
|
|
72
|
+
and printed `ok — N override(s) checked … none is failing`. It now prints
|
|
73
|
+
`could not obtain an audit report` (exit 0) on a live run, and fails (exit 1) when the failed report
|
|
74
|
+
was passed in with `--audit-file`, which is the CI path. The suppression checks still run in that
|
|
75
|
+
case, because they ask the GitHub Advisory API rather than the audit: a suppressed advisory that
|
|
76
|
+
gained a fix still fails with `FIX AVAILABLE`.
|
|
77
|
+
- The clean-tree probe reads `pnpm_config_registry`, the variable pnpm 11 audits against. It used to
|
|
78
|
+
read `npm_config_registry`, which pnpm 11 ignores, and could then ask a registry the audit never used.
|
|
79
|
+
|
|
80
|
+
`scripts/` is not part of the npm package. Projects created from nest-server-starter or lt-monorepo
|
|
81
|
+
receive these fixes with their template update.
|
|
82
|
+
|
|
83
|
+
## Troubleshooting
|
|
84
|
+
|
|
85
|
+
| Symptom | Cause | Fix |
|
|
86
|
+
| ------- | ----- | --- |
|
|
87
|
+
| Boot warning "No global json body parser is registered" | `main.ts` registers a path-scoped `json()` and no global parser | Add `server.useBodyParser('json')` to `main.ts` (§1) |
|
|
88
|
+
| Requests arrive with an empty body outside one path; sign-in reports "Missing input" | Same cause, on a version before 11.42.6 | Same fix (§1) |
|
|
89
|
+
| `check:overrides` prints `could not obtain an audit report (fetch failed)` | The npm registry was not reachable during the audit | Re-run with network access, or pass a captured report with `--audit-file` |
|
|
90
|
+
| `check:overrides` fails with `records a failed audit, not a result` | The report passed with `--audit-file` is a pnpm error, not an audit | Capture the report again once the registry answers |
|
|
91
|
+
|
|
92
|
+
## Module Documentation
|
|
93
|
+
|
|
94
|
+
- [REQUEST-LIFECYCLE.md — 0d. Body parsers](../docs/REQUEST-LIFECYCLE.md)
|
|
95
|
+
- [11.42.3 → 11.42.4 guide §1](./11.42.3-to-11.42.4.md)
|
|
96
|
+
- `.claude/rules/configurable-features.md` — Body Parser Limits row
|
|
97
|
+
- `.claude/rules/package-management.md` — the `check:overrides` guard
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "11.42.
|
|
3
|
+
"version": "11.42.6",
|
|
4
4
|
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node",
|
|
@@ -2335,6 +2335,11 @@ export interface IServerOptions {
|
|
|
2335
2335
|
* (`server.use('/upload', json({ limit }))`) is left alone, and stays the way to give ONE path a
|
|
2336
2336
|
* different limit, since the parser runs before routing and GraphQL shares a single path anyway.
|
|
2337
2337
|
*
|
|
2338
|
+
* Keep `server.useBodyParser('json')` in `main.ts` next to such a path-scoped parser. NestJS
|
|
2339
|
+
* registers its own global parser only if no layer with the same function name exists yet, so a
|
|
2340
|
+
* path-scoped `json()` registered before `app.init()` makes it skip the global one, and requests
|
|
2341
|
+
* outside that path arrive with an empty body. The initializer warns at boot when it finds that.
|
|
2342
|
+
*
|
|
2338
2343
|
* One caveat: under the testing module the options passed to `createNestApplication()` are not
|
|
2339
2344
|
* visible to providers, so a test app created with `{ rawBody: true }` gets no `req.rawBody` from
|
|
2340
2345
|
* a CONFIGURED parser. Applications created with `NestFactory.create()` are not affected.
|
|
@@ -90,11 +90,8 @@ export class CoreBodyParserInitializer implements OnModuleInit {
|
|
|
90
90
|
const limit = this.resolveLimit(type, config?.[type]?.limit);
|
|
91
91
|
const layers = stack.filter((layer) => this.isGlobalParserLayer(layer, type));
|
|
92
92
|
|
|
93
|
-
if (
|
|
94
|
-
this.
|
|
95
|
-
`bodyParser.${type}.limit is configured, but no global ${type} body parser is registered ` +
|
|
96
|
-
'(was the app created with `bodyParser: false`?) — the limit has no effect.',
|
|
97
|
-
);
|
|
93
|
+
if (!layers.length) {
|
|
94
|
+
this.warnMissingGlobalParser(type, stack, limit);
|
|
98
95
|
continue;
|
|
99
96
|
}
|
|
100
97
|
|
|
@@ -147,6 +144,18 @@ export class CoreBodyParserInitializer implements OnModuleInit {
|
|
|
147
144
|
return layer?.slash === true && typeof layer.handle === 'function' && layer.handle.name === PARSER_NAMES[type];
|
|
148
145
|
}
|
|
149
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Whether the application was created without `bodyParser: false`.
|
|
149
|
+
*
|
|
150
|
+
* Read from the same container options as `rawBody`. Under `Test.createTestingModule()` those
|
|
151
|
+
* options never reach the container, so the answer there is always "enabled".
|
|
152
|
+
*/
|
|
153
|
+
protected isBodyParserEnabled(): boolean {
|
|
154
|
+
const container = (this.moduleRef as unknown as { container?: { contextOptions?: { bodyParser?: boolean } } })
|
|
155
|
+
?.container;
|
|
156
|
+
return container?.contextOptions?.bodyParser !== false;
|
|
157
|
+
}
|
|
158
|
+
|
|
150
159
|
/**
|
|
151
160
|
* Whether the application was created with `rawBody: true`.
|
|
152
161
|
*
|
|
@@ -210,6 +219,39 @@ export class CoreBodyParserInitializer implements OnModuleInit {
|
|
|
210
219
|
);
|
|
211
220
|
}
|
|
212
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Explain why there is no global parser of this type, when there should be one.
|
|
224
|
+
*
|
|
225
|
+
* NestJS registers its own parsers inside `app.init()` only if no layer with the parser's function
|
|
226
|
+
* name exists yet (`ExpressAdapter.isMiddlewareApplied()` compares the NAME and nothing else). A
|
|
227
|
+
* path-scoped `server.use('/upload', json({ limit }))` in `main.ts` therefore counts as "applied",
|
|
228
|
+
* NestJS skips its global parser, and every request outside that path reaches its handler with an
|
|
229
|
+
* empty body — a sign-in fails with a validation error, and nothing says why. Restoring the parser
|
|
230
|
+
* from here is not safe: NestJS would have placed it after everything `main.ts` registered, and that
|
|
231
|
+
* position is no longer knowable during module init (a raw-body webhook or a proxy registered after
|
|
232
|
+
* the path-scoped parser must keep seeing the unread stream). So this reports, with the remedy.
|
|
233
|
+
*/
|
|
234
|
+
protected warnMissingGlobalParser(type: CoreBodyParserType, stack: ExpressLayer[], limit: number | undefined): void {
|
|
235
|
+
const name = PARSER_NAMES[type];
|
|
236
|
+
const shadowed = stack.some((layer) => layer?.slash !== true && layer?.handle?.name === name);
|
|
237
|
+
if (shadowed && this.isBodyParserEnabled()) {
|
|
238
|
+
this.logger.warn(
|
|
239
|
+
`No global ${type} body parser is registered: NestJS skips its own when a layer named \`${name}\` ` +
|
|
240
|
+
`already exists, and a path-scoped \`${type}()\` parser registered before \`app.init()\` counts as one. ` +
|
|
241
|
+
`Every request outside that path reaches its handler with an EMPTY body. Register the global parser ` +
|
|
242
|
+
`in main.ts as well — \`server.useBodyParser('${type}')\` — a configured \`bodyParser.${type}.limit\` ` +
|
|
243
|
+
'is applied to it.',
|
|
244
|
+
);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
if (limit !== undefined) {
|
|
248
|
+
this.logger.warn(
|
|
249
|
+
`bodyParser.${type}.limit is configured, but no global ${type} body parser is registered ` +
|
|
250
|
+
'(was the app created with `bodyParser: false`?) — the limit has no effect.',
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
213
255
|
/**
|
|
214
256
|
* Wrap a parser so its 413 is translated, keeping the name NestJS identifies the layer by.
|
|
215
257
|
*
|