@biorate/nestjs-tools 2.2.1 → 2.2.3
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/README.md +274 -5
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -1,10 +1,279 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @biorate/nestjs-tools
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Production‑grade middleware, guards, interceptors, filters, decorators, and HTTP controllers for NestJS applications built on `@biorate/inversion` and `@biorate/prometheus`.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Request counting** — Prometheus‑backed counter per method + route + status.
|
|
8
|
+
- **Response time** — Prometheus histogram + `x-response-time` header.
|
|
9
|
+
- **HTTP proxy** — Prometheus‑instrumented `http-proxy-middleware` wrapper.
|
|
10
|
+
- **Tracing** — OpenTelemetry span attributes for incoming requests.
|
|
11
|
+
- **Exception filter** — structured JSON error responses for HTTP, WS, and RPC.
|
|
12
|
+
- **Auth** — `@AuthBasic()` decorator + `AuthGuardProvider`.
|
|
13
|
+
- **Roles** — `@Roles()` decorator + `RolesGuardProvider`.
|
|
14
|
+
- **CORS** — config‑driven origin validation.
|
|
15
|
+
- **Built‑in controllers** — `/metrics`, `/probe`, `/locales`.
|
|
16
|
+
- **Hexagonal app template** — domain/application/infrastructure layers.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add @biorate/nestjs-tools
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Peer dependencies: `@nestjs/common`, `@nestjs/core`, `@nestjs/swagger`, `class-validator`, `express`, `http-proxy`, `http-proxy-middleware`, `lodash-es`, `rxjs`, `ws`.
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
4
27
|
|
|
5
|
-
#### Example:
|
|
6
28
|
```ts
|
|
7
|
-
|
|
29
|
+
import { NestFactory } from '@nestjs/core';
|
|
30
|
+
import { AppModule } from './app.module';
|
|
31
|
+
import {
|
|
32
|
+
AllExceptionsFilter,
|
|
33
|
+
BootstrapProvider,
|
|
34
|
+
RequestCountMiddleware,
|
|
35
|
+
ResponseTimeMiddleware,
|
|
36
|
+
TracingInterceptor,
|
|
37
|
+
} from '@biorate/nestjs-tools';
|
|
38
|
+
|
|
39
|
+
async function bootstrap() {
|
|
40
|
+
const app = await NestFactory.create(AppModule, { bufferLogs: true });
|
|
41
|
+
|
|
42
|
+
app.useGlobalFilters(new AllExceptionsFilter());
|
|
43
|
+
app.useGlobalInterceptors(new TracingInterceptor());
|
|
44
|
+
|
|
45
|
+
// Middleware
|
|
46
|
+
app.use(BootstrapProvider.create());
|
|
47
|
+
app.use(RequestCountMiddleware.create());
|
|
48
|
+
app.use(ResponseTimeMiddleware.create());
|
|
49
|
+
|
|
50
|
+
await app.listen(3000);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Middlewares
|
|
55
|
+
|
|
56
|
+
### `RequestCountMiddleware`
|
|
57
|
+
|
|
58
|
+
Prometheus counter `http_server_requests_seconds_count` with labels `method`, `uri`, `status`.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
consumer.apply(RequestCountMiddleware).forRoutes('*');
|
|
62
|
+
// or via BootstrapProvider
|
|
63
|
+
app.use(RequestCountMiddleware.create());
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Uses `RoutesInterceptor` to resolve the actual route path (e.g. `/api/users/:id` instead of `/api/users/123`).
|
|
67
|
+
|
|
68
|
+
Config option: `app.middleware.RequestCountMiddleware.log-base-url` (boolean, default `false`).
|
|
69
|
+
|
|
70
|
+
### `ResponseTimeMiddleware`
|
|
71
|
+
|
|
72
|
+
Prometheus histogram `http_server_requests_seconds` + `x-response-time` header.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
app.use(ResponseTimeMiddleware.create());
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Config options:
|
|
79
|
+
|
|
80
|
+
| Path | Default |
|
|
81
|
+
|-----------------------------------------------------|-------------|
|
|
82
|
+
| `app.middleware.ResponseTimeMiddleware.suffix` | `true` |
|
|
83
|
+
| `app.middleware.ResponseTimeMiddleware.header` | `x-response-time` |
|
|
84
|
+
| `app.middleware.ResponseTimeMiddleware.digits` | `3` |
|
|
85
|
+
| `app.middleware.ResponseTimeMiddleware.log-base-url` | `false` |
|
|
86
|
+
| `ResponseTimeMiddleware.histogram.buckets` | `[0.005, 0.01, … , 10]` |
|
|
87
|
+
|
|
88
|
+
### `ProxyPrometheusMiddleware`
|
|
89
|
+
|
|
90
|
+
Prometheus‑instrumented HTTP proxy via `http-proxy-middleware`.
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { ProxyPrometheusMiddleware } from '@biorate/nestjs-tools';
|
|
94
|
+
|
|
95
|
+
consumer.apply(ProxyPrometheusMiddleware.create({
|
|
96
|
+
target: 'http://upstream.service:8080',
|
|
97
|
+
changeOrigin: true,
|
|
98
|
+
})).forRoutes('/api/v2/legacy');
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Creates two metrics:
|
|
102
|
+
- `http_proxy_server_requests_seconds_count` (counter)
|
|
103
|
+
- `http_proxy_server_requests_seconds` (histogram)
|
|
104
|
+
|
|
105
|
+
Config: `ProxyPrometheusMiddleware.histogram.buckets`.
|
|
106
|
+
|
|
107
|
+
## Filters
|
|
108
|
+
|
|
109
|
+
### `AllExceptionsFilter`
|
|
110
|
+
|
|
111
|
+
Catches all exceptions and returns a structured JSON response:
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"status": 402,
|
|
116
|
+
"code": "PaymentError",
|
|
117
|
+
"hint": "Insufficient funds",
|
|
118
|
+
"path": "/api/orders",
|
|
119
|
+
"meta": {}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Supports HTTP, WebSocket, and RPC transport types. Maps `meta.status` from `@biorate/errors` to HTTP status codes.
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
app.useGlobalFilters(new AllExceptionsFilter());
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Interceptors
|
|
130
|
+
|
|
131
|
+
### `TracingInterceptor`
|
|
132
|
+
|
|
133
|
+
Adds OpenTelemetry span attributes for incoming requests:
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
incoming.request.url
|
|
137
|
+
incoming.request.body
|
|
138
|
+
incoming.request.headers
|
|
139
|
+
incoming.request.method
|
|
140
|
+
incoming.request.params
|
|
141
|
+
incoming.request.query
|
|
142
|
+
incoming.response.headers
|
|
143
|
+
incoming.response.statusCode
|
|
144
|
+
incoming.response.data
|
|
145
|
+
incoming.response.errorCode
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Config: `TracingInterceptor.excluded` — array of strings or RegExp to skip URLs.
|
|
149
|
+
|
|
150
|
+
### `RoutesInterceptor`
|
|
151
|
+
|
|
152
|
+
Resolves the full route path (controller + handler) and stores it in a `WeakMap<Request, { path }>` for use by `RequestCountMiddleware` and `ResponseTimeMiddleware`.
|
|
153
|
+
|
|
154
|
+
Applied automatically when using the count/time middlewares.
|
|
155
|
+
|
|
156
|
+
## Providers (Guards)
|
|
157
|
+
|
|
158
|
+
### `AuthGuardProvider`
|
|
159
|
+
|
|
160
|
+
HTTP Basic Authentication guard. Validates `Authorization: Basic …` against config `app.auth.basic` (map of `username: password`). If config is absent, access is granted.
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
@Injectable()
|
|
164
|
+
export class MyController {
|
|
165
|
+
@Get('/admin')
|
|
166
|
+
@AuthBasic()
|
|
167
|
+
public admin() { return 'secret data'; }
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### `RolesGuardProvider`
|
|
172
|
+
|
|
173
|
+
Bitmask‑based role guard. Use with `@Roles()` decorator:
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { Roles, UserRoles } from '@biorate/nestjs-tools';
|
|
177
|
+
|
|
178
|
+
@Roles(UserRoles.Admin | UserRoles.Manager)
|
|
179
|
+
@Get('/admin')
|
|
180
|
+
public admin() { return 'ok'; }
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Override `getUser()` to load the user from your database:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
export class CustomRolesGuard extends RolesGuardProvider {
|
|
187
|
+
protected async getUser(context: ExecutionContext) {
|
|
188
|
+
const request = context.switchToHttp().getRequest();
|
|
189
|
+
const user = await myDb.findUser(request.cookies.sessionId);
|
|
190
|
+
return user;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### `BootstrapProvider`
|
|
196
|
+
|
|
197
|
+
Convenience middleware that initializes the `@biorate/inversion` container lifecycle:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
app.use(BootstrapProvider.create());
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Decorators
|
|
204
|
+
|
|
205
|
+
| Decorator | Composition |
|
|
206
|
+
|---------------|--------------------------------------------------|
|
|
207
|
+
| `@Roles(...)` | `SetMetadata(ROLES_KEY, bitmask)` |
|
|
208
|
+
| `@AuthBasic()`| `ApiBasicAuth()` + `UseGuards(AuthGuardProvider)` |
|
|
209
|
+
|
|
210
|
+
## CORS
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
import { corsOriginHandler } from '@biorate/nestjs-tools';
|
|
214
|
+
|
|
215
|
+
app.enableCors({ origin: corsOriginHandler });
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
In non‑debug environments, validates the origin against `ALLOWED_ORIGIN` config regexp (defaults to `package.name` without `-server` suffix). In `debug` env, all origins are allowed.
|
|
219
|
+
|
|
220
|
+
## HTTP controllers
|
|
221
|
+
|
|
222
|
+
### `MetricsController`
|
|
223
|
+
|
|
224
|
+
Exposes `GET /metrics` with Prometheus metrics from the shared registry.
|
|
225
|
+
|
|
226
|
+
### `ProbeController`
|
|
227
|
+
|
|
228
|
+
| Route | Purpose |
|
|
229
|
+
|-----------------|---------------------|
|
|
230
|
+
| `GET /probe` | Liveness/readiness |
|
|
231
|
+
|
|
232
|
+
### `LocalesController`
|
|
233
|
+
|
|
234
|
+
Serves locale files for internationalization.
|
|
235
|
+
|
|
236
|
+
## App structure (hexagonal)
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
src/app/
|
|
240
|
+
├── application/
|
|
241
|
+
│ ├── ports/ ← Use‑case interfaces (inbound ports)
|
|
242
|
+
│ └── service/ ← Use‑case implementations
|
|
243
|
+
├── domain/
|
|
244
|
+
│ └── .keep ← Domain models, entities, value objects
|
|
245
|
+
└── infrastructure/
|
|
246
|
+
├── adapters/ ← Outbound port implementations
|
|
247
|
+
└── controllers/ ← NestJS controllers
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Architecture
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
┌──────────────────────────────────────────────────────┐
|
|
254
|
+
│ Incoming HTTP │
|
|
255
|
+
└──────────┬───────────────────────────────────────────┘
|
|
256
|
+
│
|
|
257
|
+
┌──────────▼───────────────────────────────────────────┐
|
|
258
|
+
│ BootstrapProvider (DI lifecycle init) │
|
|
259
|
+
├──────────────────────────────────────────────────────┤
|
|
260
|
+
│ RequestCountMiddleware (counter) │
|
|
261
|
+
├──────────────────────────────────────────────────────┤
|
|
262
|
+
│ ResponseTimeMiddleware (histogram + header) │
|
|
263
|
+
├──────────────────────────────────────────────────────┤
|
|
264
|
+
│ RoutesInterceptor (path resolution) │
|
|
265
|
+
├──────────────────────────────────────────────────────┤
|
|
266
|
+
│ TracingInterceptor (OpenTelemetry span attrs) │
|
|
267
|
+
├──────────────────────────────────────────────────────┤
|
|
268
|
+
│ AllExceptionsFilter (structured error JSON) │
|
|
269
|
+
├──────────────────────────────────────────────────────┤
|
|
270
|
+
│ AuthGuardProvider / RolesGuardProvider │
|
|
271
|
+
├──────────────────────────────────────────────────────┤
|
|
272
|
+
│ ┌──────────────────────┐ │
|
|
273
|
+
│ │ ProxyMiddleware │ (optional) │
|
|
274
|
+
│ │ │ │
|
|
275
|
+
│ └──────────────────────┘ │
|
|
276
|
+
└──────────────────────────────────────────────────────┘
|
|
8
277
|
```
|
|
9
278
|
|
|
10
279
|
### Learn
|
|
@@ -15,7 +284,7 @@ This module provides an middlewares, providers, filters ans decorators for NestJ
|
|
|
15
284
|
|
|
16
285
|
See the [CHANGELOG](https://github.com/biorate/core/blob/master/packages/%40biorate/nestjs-tools/CHANGELOG.md)
|
|
17
286
|
|
|
18
|
-
|
|
287
|
+
## License
|
|
19
288
|
|
|
20
289
|
[MIT](https://github.com/biorate/core/blob/master/packages/%40biorate/nestjs-tools/LICENSE)
|
|
21
290
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@biorate/nestjs-tools",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"description": "NestJS tools",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"keywords": [],
|
|
46
46
|
"author": "llevkin",
|
|
47
47
|
"license": "MIT",
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "ff1dcf14a733ce8004ca582d2c421a2154ce97cb",
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@nestjs/common": ">=10.4.15",
|
|
51
51
|
"@nestjs/core": ">=10.4.15",
|
|
@@ -59,13 +59,13 @@
|
|
|
59
59
|
"ws": ">=8.18.0"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@biorate/axios": "^3.3.
|
|
63
|
-
"@biorate/config": "^3.2.
|
|
64
|
-
"@biorate/errors": "^3.1.
|
|
65
|
-
"@biorate/inversion": "^3.1.
|
|
66
|
-
"@biorate/opentelemetry": "^2.1
|
|
67
|
-
"@biorate/prometheus": "^3.1.
|
|
68
|
-
"@biorate/tools": "^3.1.
|
|
62
|
+
"@biorate/axios": "^3.3.1",
|
|
63
|
+
"@biorate/config": "^3.2.1",
|
|
64
|
+
"@biorate/errors": "^3.1.1",
|
|
65
|
+
"@biorate/inversion": "^3.1.1",
|
|
66
|
+
"@biorate/opentelemetry": "^2.2.1",
|
|
67
|
+
"@biorate/prometheus": "^3.1.1",
|
|
68
|
+
"@biorate/tools": "^3.1.1",
|
|
69
69
|
"http-proxy": "1.18.1",
|
|
70
70
|
"http-proxy-middleware": "2.0.6",
|
|
71
71
|
"json-stringify-safe": "5.0.1",
|