@lenne.tech/nest-server 11.20.1 → 11.21.1

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.
Files changed (84) hide show
  1. package/README.md +444 -100
  2. package/dist/core/common/decorators/restricted.decorator.d.ts +1 -0
  3. package/dist/core/common/decorators/restricted.decorator.js +4 -1
  4. package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
  5. package/dist/core/common/helpers/input.helper.js +11 -8
  6. package/dist/core/common/helpers/input.helper.js.map +1 -1
  7. package/dist/core/common/interceptors/check-security.interceptor.js +10 -8
  8. package/dist/core/common/interceptors/check-security.interceptor.js.map +1 -1
  9. package/dist/core/common/interfaces/server-options.interface.d.ts +5 -1
  10. package/dist/core/common/middleware/request-context.middleware.js +10 -6
  11. package/dist/core/common/middleware/request-context.middleware.js.map +1 -1
  12. package/dist/core/common/plugins/mongoose-tenant.plugin.js +40 -24
  13. package/dist/core/common/plugins/mongoose-tenant.plugin.js.map +1 -1
  14. package/dist/core/common/services/email.service.d.ts +5 -1
  15. package/dist/core/common/services/email.service.js +16 -2
  16. package/dist/core/common/services/email.service.js.map +1 -1
  17. package/dist/core/common/services/request-context.service.d.ts +3 -0
  18. package/dist/core/common/services/request-context.service.js +6 -0
  19. package/dist/core/common/services/request-context.service.js.map +1 -1
  20. package/dist/core/modules/auth/guards/roles.guard.js +6 -10
  21. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  22. package/dist/core/modules/auth/tokens.decorator.d.ts +1 -1
  23. package/dist/core/modules/better-auth/better-auth-roles.guard.js +5 -6
  24. package/dist/core/modules/better-auth/better-auth-roles.guard.js.map +1 -1
  25. package/dist/core/modules/better-auth/core-better-auth-user.mapper.d.ts +6 -0
  26. package/dist/core/modules/better-auth/core-better-auth-user.mapper.js +52 -17
  27. package/dist/core/modules/better-auth/core-better-auth-user.mapper.js.map +1 -1
  28. package/dist/core/modules/better-auth/core-better-auth.service.d.ts +3 -1
  29. package/dist/core/modules/better-auth/core-better-auth.service.js +14 -0
  30. package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
  31. package/dist/core/modules/tenant/core-tenant-member.model.d.ts +11 -0
  32. package/dist/core/modules/tenant/core-tenant-member.model.js +106 -0
  33. package/dist/core/modules/tenant/core-tenant-member.model.js.map +1 -0
  34. package/dist/core/modules/tenant/core-tenant.decorators.d.ts +3 -0
  35. package/dist/core/modules/tenant/core-tenant.decorators.js +12 -0
  36. package/dist/core/modules/tenant/core-tenant.decorators.js.map +1 -0
  37. package/dist/core/modules/tenant/core-tenant.enums.d.ts +13 -0
  38. package/dist/core/modules/tenant/core-tenant.enums.js +25 -0
  39. package/dist/core/modules/tenant/core-tenant.enums.js.map +1 -0
  40. package/dist/core/modules/tenant/core-tenant.guard.d.ts +25 -0
  41. package/dist/core/modules/tenant/core-tenant.guard.js +271 -0
  42. package/dist/core/modules/tenant/core-tenant.guard.js.map +1 -0
  43. package/dist/core/modules/tenant/core-tenant.helpers.d.ts +7 -0
  44. package/dist/core/modules/tenant/core-tenant.helpers.js +60 -0
  45. package/dist/core/modules/tenant/core-tenant.helpers.js.map +1 -0
  46. package/dist/core/modules/tenant/core-tenant.module.d.ts +12 -0
  47. package/dist/core/modules/tenant/core-tenant.module.js +58 -0
  48. package/dist/core/modules/tenant/core-tenant.module.js.map +1 -0
  49. package/dist/core/modules/tenant/core-tenant.service.d.ts +19 -0
  50. package/dist/core/modules/tenant/core-tenant.service.js +170 -0
  51. package/dist/core/modules/tenant/core-tenant.service.js.map +1 -0
  52. package/dist/core/modules/user/core-user.service.js +12 -1
  53. package/dist/core/modules/user/core-user.service.js.map +1 -1
  54. package/dist/core.module.js +11 -0
  55. package/dist/core.module.js.map +1 -1
  56. package/dist/index.d.ts +7 -0
  57. package/dist/index.js +7 -0
  58. package/dist/index.js.map +1 -1
  59. package/dist/tsconfig.build.tsbuildinfo +1 -1
  60. package/package.json +35 -24
  61. package/src/core/common/decorators/restricted.decorator.ts +12 -2
  62. package/src/core/common/helpers/input.helper.ts +24 -9
  63. package/src/core/common/interceptors/check-security.interceptor.ts +19 -13
  64. package/src/core/common/interfaces/server-options.interface.ts +80 -28
  65. package/src/core/common/middleware/request-context.middleware.ts +12 -5
  66. package/src/core/common/plugins/mongoose-tenant.plugin.ts +78 -45
  67. package/src/core/common/services/email.service.ts +26 -5
  68. package/src/core/common/services/request-context.service.ts +15 -1
  69. package/src/core/modules/auth/guards/roles.guard.ts +10 -10
  70. package/src/core/modules/better-auth/better-auth-roles.guard.ts +9 -6
  71. package/src/core/modules/better-auth/core-better-auth-user.mapper.ts +86 -21
  72. package/src/core/modules/better-auth/core-better-auth.service.ts +27 -2
  73. package/src/core/modules/tenant/INTEGRATION-CHECKLIST.md +165 -0
  74. package/src/core/modules/tenant/README.md +268 -0
  75. package/src/core/modules/tenant/core-tenant-member.model.ts +121 -0
  76. package/src/core/modules/tenant/core-tenant.decorators.ts +46 -0
  77. package/src/core/modules/tenant/core-tenant.enums.ts +77 -0
  78. package/src/core/modules/tenant/core-tenant.guard.ts +441 -0
  79. package/src/core/modules/tenant/core-tenant.helpers.ts +103 -0
  80. package/src/core/modules/tenant/core-tenant.module.ts +102 -0
  81. package/src/core/modules/tenant/core-tenant.service.ts +244 -0
  82. package/src/core/modules/user/core-user.service.ts +17 -1
  83. package/src/core.module.ts +15 -0
  84. package/src/index.ts +12 -0
package/README.md CHANGED
@@ -1,165 +1,509 @@
1
1
  # lenne.Tech Nest Server
2
2
 
3
- Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB
4
- (or other databases).
3
+ An enterprise-grade extension layer on top of [NestJS](https://nestjs.com/) for building secure, scalable server applications with **GraphQL**, **REST/Swagger**, and **MongoDB**.
5
4
 
6
- The lenne.tech nest server can be included as an npm package (`npm i @lenne.tech/nest-server`) or used directly as a
7
- project (`git clone https://github.com/lenneTech/nest-server.git`).
5
+ [![License](https://img.shields.io/github/license/lenneTech/nest-server)](/LICENSE)
6
+ [![npm version](https://img.shields.io/npm/v/@lenne.tech/nest-server)](https://www.npmjs.com/package/@lenne.tech/nest-server)
7
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D%2020-brightgreen)](https://nodejs.org/)
8
+
9
+ ## Table of Contents
10
+
11
+ - [Quick Start](#quick-start)
12
+ - [Features Overview](#features-overview)
13
+ - [Architecture](#architecture)
14
+ - [API-First Design](#api-first-design)
15
+ - [Authentication](#authentication)
16
+ - [Roles & Permissions](#roles--permissions)
17
+ - [Multi-Tenancy](#multi-tenancy)
18
+ - [Scalability](#scalability)
19
+ - [API Versioning](#api-versioning)
20
+ - [Webhook Support](#webhook-support)
21
+ - [External System Integration](#external-system-integration)
22
+ - [Core Modules](#core-modules)
23
+ - [Configuration](#configuration)
24
+ - [Development](#development)
25
+ - [Documentation](#documentation)
26
+ - [License](#license)
27
+
28
+ ## Quick Start
29
+
30
+ The fastest way to get started is via the [lenne.Tech CLI](https://github.com/lenneTech/cli) with the [starter project](https://github.com/lenneTech/nest-server-starter):
8
31
 
9
- In combination with Angular (see [lenne.Tech Angular example](https://github.com/lenneTech/angular-example)
10
- incl. [ng-base](https://github.com/lenneTech/ng-base/tree/main/projects/ng-base/README.md)) the Nest Server is an ideal
11
- basis for your next project.
32
+ ```bash
33
+ npm install -g @lenne.tech/cli
34
+ lt server create <ServerName>
35
+ cd <ServerName>
36
+ pnpm start
37
+ ```
12
38
 
13
- [![License](https://img.shields.io/github/license/lenneTech/nest-server)](/LICENSE)
39
+ Or install directly as a package:
14
40
 
15
- ## Set up your server
41
+ ```bash
42
+ pnpm add @lenne.tech/nest-server
43
+ ```
16
44
 
17
- The easiest way to set up your own server based on the lenne.Tech Nest Server is to use the
18
- [lenne.Tech Nest Server starter kit](https://github.com/lenneTech/nest-server-starter) via [CLI](https://github.com/lenneTech/cli):
45
+ ### Create a new module
19
46
 
47
+ ```bash
48
+ lt server module <ModuleName>
20
49
  ```
21
- $ npm install -g @lenne.tech/cli
22
- $ lt server create <ServerName>
23
- $ cd <ServerName>
50
+
51
+ This generates a complete module with model, inputs, resolver, controller, and service.
52
+
53
+ ## Features Overview
54
+
55
+ | Category | Features |
56
+ |----------|----------|
57
+ | **API-First** | GraphQL (Apollo Server) + REST with Swagger/OpenAPI, dual API surface from single codebase |
58
+ | **Authentication** | BetterAuth (IAM) with JWT, sessions, 2FA/TOTP, Passkey/WebAuthn, social login; Legacy JWT auth |
59
+ | **Authorization** | Extensible role-based access control, field-level restrictions, system roles, hierarchy roles |
60
+ | **Database** | MongoDB via Mongoose, CrudService with security pipeline, advanced filtering & pagination |
61
+ | **Multi-Tenancy** | Header-based tenant isolation, membership management, hierarchy roles, auto-filtering |
62
+ | **File Handling** | GridFS file storage, resumable uploads via TUS protocol (up to 50 GB) |
63
+ | **Security** | Defense-in-depth: input validation, password hashing, role guard, audit fields, response filtering |
64
+ | **Integration** | SCIM support, OAuth providers, multi-provider email (Mailjet/Brevo/SMTP), webhook support via lifecycle hooks |
65
+ | **Scalability** | Stateless design, distributed migration locking, request-scoped isolation, query complexity analysis |
66
+ | **API Versioning** | NestJS-native URI/header/media-type versioning, GraphQL schema evolution with deprecations |
67
+ | **DevOps** | Health checks, database migrations, cron jobs, permissions reporting, system setup |
68
+
69
+ ## Architecture
70
+
71
+ ### Modular Extensibility
72
+
73
+ The framework is built on a **Module Inheritance Pattern** — all core modules are designed to be extended through class inheritance in consuming projects:
74
+
75
+ ```typescript
76
+ // Your project extends core classes — full control via override + super()
77
+ export class UserService extends CoreUserService {
78
+ override async create(input, serviceOptions) {
79
+ // Custom pre-processing (validation, enrichment, ...)
80
+ const result = await super.create(input, serviceOptions);
81
+ // Custom post-processing (notifications, logging, ...)
82
+ return result;
83
+ }
84
+ }
24
85
  ```
25
86
 
26
- ## Description
87
+ **Why inheritance over hooks/events:**
88
+ - **Full control**: Override methods and precisely define what happens before/after `super()` calls
89
+ - **Selective override**: Skip parts of the parent implementation entirely for custom logic
90
+ - **Type safety**: TypeScript inheritance ensures proper typing and IDE support
91
+ - **No silent failures**: No runtime event systems or hooks that can fail silently
27
92
 
28
- The lenne.Tech **Nest Server** is based on the [Nest](https://github.com/nestjs/nest) framework and can either be used
29
- and extended as a boilerplate (git clone) or integrated as a module (npm package).
93
+ ### Two-Layer Structure
30
94
 
31
- Since the server is based on [Nest](https://nestjs.com/), you can find all information about extending your server
32
- in the [documentation of Nest](https://docs.nestjs.com/).
95
+ - `src/core/` Reusable framework components (exported via npm, extended by projects)
96
+ - `src/server/` Internal test/demo implementation (not exported)
33
97
 
34
- We use Mongoose Module from nestjs. (https://docs.nestjs.com/techniques/mongodb)
98
+ Every core module supports both **auto-registration** (zero-config) and **manual registration** (full customization via extended module). New modules can be added alongside existing ones without modifying the framework.
35
99
 
36
- To create a new Module with model, inputs, resolver and service you can use the [CLI](https://github.com/lenneTech/cli):
100
+ ### Framework Stack
37
101
 
102
+ - [NestJS](https://nestjs.com/) — Server framework
103
+ - [Apollo Server](https://www.apollographql.com/docs/apollo-server/) — GraphQL API (optional, disable via `graphQl: false`)
104
+ - [Mongoose](https://mongoosejs.com/) — MongoDB ODM
105
+ - [Swagger/OpenAPI](https://swagger.io/) — REST API documentation
106
+
107
+ ## API-First Design
108
+
109
+ The server follows an API-first approach where API contracts are the primary interface:
110
+
111
+ ### Dual API Surface
112
+
113
+ Both **GraphQL** and **REST** endpoints are served from the same codebase and business logic. The `@UnifiedField()` decorator generates schemas for both APIs simultaneously:
114
+
115
+ ```typescript
116
+ @UnifiedField({ description: 'User email address', isOptional: false })
117
+ email: string;
118
+ // → Generates: GraphQL @Field() + Swagger @ApiProperty() + class-validator checks
38
119
  ```
39
- $ lt server module <ModuleName>
120
+
121
+ ### Documented API Structure
122
+
123
+ - **GraphQL Introspection** — Full schema introspection for client code generation (configurable via `graphQl.introspection`)
124
+ - **Swagger/OpenAPI** — Auto-generated REST API documentation from decorators
125
+ - **SpectaQL** — Visual GraphQL documentation (`pnpm run docs`)
126
+ - **Permissions Dashboard** — Interactive HTML report of all endpoints, roles, and security checks (`/permissions`)
127
+
128
+ ### GraphQL Features
129
+
130
+ - Custom scalars: `Date`, `DateTime`, `JSON`, `Any`
131
+ - WebSocket subscriptions with authentication
132
+ - Query complexity analysis (DoS prevention)
133
+ - File upload support via `graphql-upload`
134
+ - Automatic enum registration
135
+
136
+ ## API Versioning
137
+
138
+ NestJS provides built-in [API versioning](https://docs.nestjs.com/techniques/versioning) support with multiple strategies. Projects can enable versioning for REST endpoints:
139
+
140
+ | Strategy | Example | Use Case |
141
+ |----------|---------|----------|
142
+ | **URI** | `/v1/users`, `/v2/users` | Most common, explicit in URL |
143
+ | **Header** | `X-API-Version: 2` | Clean URLs, version in header |
144
+ | **Media Type** | `Accept: application/vnd.app.v2+json` | Content negotiation |
145
+
146
+ ```typescript
147
+ // main.ts — enable URI versioning
148
+ app.enableVersioning({ type: VersioningType.URI });
149
+
150
+ // controller — assign to version
151
+ @Controller({ path: 'users', version: '2' })
152
+ export class UsersV2Controller { ... }
153
+
154
+ // or per-route
155
+ @Get()
156
+ @Version('2')
157
+ async findAll() { ... }
40
158
  ```
41
159
 
42
- We are currently working on a documentation of the extensions and auxiliary classes that the
43
- [lenne.Tech Nest Server](https://github.com/lenneTech/nest-server) contains. As long as this is not yet available,
44
- have a look at the [source code](https://github.com/lenneTech/nest-server/tree/master/src/core).
45
- There you will find a lot of things that will help you to extend your server, such as:
160
+ **GraphQL** APIs are evolved through schema additions and field deprecations (`@deprecated`) rather than versioning, following the [GraphQL best practice](https://graphql.org/learn/best-practices/#versioning) of continuous evolution.
46
161
 
47
- - [GraphQL scalars](https://github.com/lenneTech/nest-server/tree/master/src/core/common/scalars)
48
- - [Filter and pagination](https://github.com/lenneTech/nest-server/tree/master/src/core/common/args)
49
- - [Decorators for restrictions and roles](https://github.com/lenneTech/nest-server/tree/master/src/core/common/decorators)
50
- - [Authorisation handling](https://github.com/lenneTech/nest-server/tree/master/src/core/modules/auth)
51
- - [Ready to use user module](https://github.com/lenneTech/nest-server/tree/master/src/core/modules/user)
52
- - [Common helpers](https://github.com/lenneTech/nest-server/tree/master/src/core/common/helpers) and
53
- [helpers for tests](https://github.com/lenneTech/nest-server/blob/master/src/test/test.helper.ts)
54
- - ...
162
+ ## Webhook Support
55
163
 
56
- ## Running the server
164
+ The framework provides webhook capabilities through two mechanisms:
57
165
 
58
- ```bash
59
- # development
60
- $ pnpm start
166
+ ### BetterAuth Lifecycle Hooks
61
167
 
62
- # watch mode
63
- $ pnpm run start:dev
168
+ BetterAuth supports [hooks](https://www.better-auth.com/docs/concepts/hooks) that intercept authentication lifecycle events. Projects can extend the BetterAuth service via the Module Inheritance Pattern to react to these events:
64
169
 
65
- # production mode
66
- $ pnpm run start:prod
170
+ ```typescript
171
+ export class AuthService extends CoreBetterAuthService {
172
+ override async signIn(input, serviceOptions) {
173
+ const result = await super.signIn(input, serviceOptions);
174
+ // Trigger webhook after successful sign-in
175
+ await this.webhookService.dispatch('auth.sign-in', { userId: result.user.id });
176
+ return result;
177
+ }
178
+ }
67
179
  ```
68
180
 
181
+ Authentication events that can be intercepted include: sign-in, sign-up, sign-out, session creation, token refresh, email verification, 2FA verification, and more.
69
182
 
70
- ## Test
183
+ ### Module Inheritance for Custom Webhooks
71
184
 
72
- ```bash
73
- # unit tests
74
- $ pnpm test
185
+ Any service method can be extended to dispatch webhooks via the Module Inheritance Pattern:
186
+
187
+ ```typescript
188
+ export class ProjectService extends CoreProjectService {
189
+ override async create(input, serviceOptions) {
190
+ const project = await super.create(input, serviceOptions);
191
+ // Dispatch webhook on project creation
192
+ await this.webhookService.dispatch('project.created', project);
193
+ return project;
194
+ }
195
+ }
196
+ ```
197
+
198
+ This approach allows projects to add webhook dispatching to any CRUD operation or custom business logic without modifying the framework.
199
+
200
+ ## Authentication
201
+
202
+ Two authentication systems are available — BetterAuth (recommended) and Legacy JWT auth:
203
+
204
+ ### BetterAuth (IAM) — Recommended
205
+
206
+ Modern, session-based authentication with plugin architecture:
207
+
208
+ | Feature | Config |
209
+ |---------|--------|
210
+ | **JWT tokens** | `betterAuth.jwt: true` |
211
+ | **Two-Factor (2FA/TOTP)** | `betterAuth.twoFactor: true` |
212
+ | **Passkey/WebAuthn** | `betterAuth.passkey: true` |
213
+ | **Social login** (Google, GitHub, Apple, ...) | `betterAuth.socialProviders: { ... }` |
214
+ | **Email verification** | `betterAuth.emailVerification: { ... }` |
215
+ | **Rate limiting** | `betterAuth.rateLimit: { max: 10 }` |
216
+ | **Cross-subdomain cookies** | `betterAuth.crossSubDomainCookies: true` |
217
+ | **Disable sign-up** | `betterAuth.emailAndPassword.disableSignUp: true` |
218
+
219
+ Three integration patterns: zero-config, config-based, or manual (`autoRegister: false`).
220
+
221
+ BetterAuth provides **lifecycle hooks** for reacting to authentication events (sign-in, sign-up, session creation, etc.), enabling integration with external systems. See [Webhook Support](#webhook-support) for details.
222
+
223
+ ### Legacy Auth (JWT)
224
+
225
+ Passport-based JWT authentication with sign-in, sign-up, refresh tokens, and rate limiting. Runs in parallel with BetterAuth during migration. Legacy endpoints can be disabled via `auth.legacyEndpoints.enabled: false`.
226
+
227
+ A built-in **migration status query** (`betterAuthMigrationStatus`) tracks progress and indicates when legacy auth can be safely disabled.
228
+
229
+ ## Roles & Permissions
230
+
231
+ The role and permission system is designed for extensibility — from simple role checks to complex multi-tenant hierarchies.
232
+
233
+ ### System Roles (Runtime Checks)
234
+
235
+ System roles (`S_` prefix) are evaluated dynamically at runtime, never stored in the database:
236
+
237
+ | Role | Purpose |
238
+ |------|---------|
239
+ | `S_USER` | Any authenticated user |
240
+ | `S_VERIFIED` | Email-verified users |
241
+ | `S_CREATOR` | Creator of the resource |
242
+ | `S_SELF` | User accessing own data |
243
+ | `S_EVERYONE` | Public access |
244
+ | `S_NO_ONE` | Permanently locked |
245
+
246
+ ### Method & Field-Level Authorization
247
+
248
+ ```typescript
249
+ // Method-level: who can call this endpoint?
250
+ @Roles(RoleEnum.ADMIN)
251
+ async deleteUser() { ... }
252
+
253
+ // Field-level: who can see/modify this field?
254
+ @Restricted(RoleEnum.S_SELF, RoleEnum.ADMIN)
255
+ email: string;
256
+
257
+ // Membership-based: only team members can see this field
258
+ @Restricted({ memberOf: 'teamMembers' })
259
+ internalNotes: string;
260
+ ```
75
261
 
76
- # e2e tests
77
- $ pnpm run test:e2e
262
+ ### Custom Role Hierarchies
78
263
 
79
- # test coverage
80
- $ pnpm run test:cov
264
+ Projects can define custom role hierarchies beyond the built-in roles:
265
+
266
+ ```typescript
267
+ // Custom hierarchy with level-based comparison
268
+ const HR = createHierarchyRoles({ viewer: 1, editor: 2, admin: 3, owner: 4 });
269
+
270
+ @Roles(HR.EDITOR) // requires level >= 2 (editor, admin, or owner)
271
+ async editDocument() { ... }
81
272
  ```
82
273
 
83
- Configuration for testing:
274
+ ### Resource-Level Security
275
+
276
+ Every model can override `securityCheck()` for fine-grained, context-aware access control:
277
+
278
+ ```typescript
279
+ export class Project extends CorePersistenceModel {
280
+ override securityCheck(user: any, force?: boolean): this {
281
+ // Remove sensitive fields based on user context
282
+ if (!this.hasRole(user, RoleEnum.ADMIN)) {
283
+ this.budget = undefined;
284
+ }
285
+ return this;
286
+ }
287
+ }
84
288
  ```
85
- Node interpreter: /user/local/bin/node
86
- Working directory: FULL_PATH_TO_PROJECT_DIR
87
- Test command: pnpm run test:e2e
289
+
290
+ ### Permissions Reporting
291
+
292
+ The built-in Permissions module scans all endpoints and generates security audit reports (HTML dashboard, JSON, Markdown) showing coverage of `@Roles`, `@Restricted`, and `securityCheck()` across the entire API.
293
+
294
+ ## Multi-Tenancy
295
+
296
+ Full multi-tenancy support with header-based tenant isolation, membership management, and automatic data filtering:
297
+
298
+ ```typescript
299
+ // config.env.ts
300
+ multiTenancy: {
301
+ headerName: 'x-tenant-id',
302
+ roleHierarchy: { member: 1, manager: 2, owner: 3 },
303
+ adminBypass: true,
304
+ }
305
+
306
+ // Usage in resolvers
307
+ @Roles(DefaultHR.MANAGER)
308
+ async updateProject(@CurrentTenant() tenantId: string) { ... }
88
309
  ```
89
310
 
90
- ## Debugging
311
+ - **Membership validation** — `CoreTenantGuard` validates membership on every request
312
+ - **Hierarchy roles** — Level comparison (higher includes lower), custom via `createHierarchyRoles()`
313
+ - **Automatic data isolation** — Mongoose tenant plugin filters all queries by tenant context
314
+ - **Defense-in-depth** — Guard validation + Mongoose plugin Safety Net combination
315
+ - **Multi-membership** — Users can belong to multiple tenants with different roles
316
+ - **Status management** — ACTIVE, INVITED, SUSPENDED membership states
317
+ - **`@SkipTenantCheck()`** — Opt out per endpoint
318
+ - **`@CurrentTenant()`** — Parameter decorator for validated tenant ID
319
+
320
+ ## Scalability
321
+
322
+ The architecture is designed for horizontal scalability:
323
+
324
+ - **Stateless request handling** — No server-side session state required (JWT or BetterAuth sessions in MongoDB)
325
+ - **Request-scoped isolation** — `AsyncLocalStorage`-based `RequestContext` ensures safe concurrent request processing without shared mutable state
326
+ - **Distributed migration locking** — MongoDB-based distributed locks via `synchronizedMigration()` for safe multi-instance deployments
327
+ - **Query complexity analysis** — Configurable complexity limits prevent expensive GraphQL queries from consuming excessive resources
328
+ - **Connection pooling** — Managed transparently via Mongoose/MongoDB driver
329
+ - **Configurable MongoDB** — Support for replica sets and connection options via `mongoose.uri` configuration
91
330
 
92
- Configuration for debugging is:
331
+ ## External System Integration
332
+
333
+ ### Authentication Providers
334
+
335
+ BetterAuth supports OAuth integration with external identity providers (Google, GitHub, Apple, Discord, and more) via the `socialProviders` configuration.
336
+
337
+ ### SCIM Support
338
+
339
+ Built-in [SCIM](http://www.simplecloud.info/) (System for Cross-domain Identity Management) filter parsing and MongoDB query conversion for enterprise identity management:
340
+
341
+ ```typescript
342
+ scimToMongo('userName eq "Joe" and emails[type eq "work"]')
343
+ // → MongoDB: { $and: [{ userName: 'Joe' }, { emails: { $elemMatch: { type: 'work' } } }] }
93
344
  ```
94
- Node interpreter: /user/local/bin/node
95
- Node parameters: node_modules/@nestjs/cli/bin/nest.js start --debug --watch
96
- Working directory: FULL_PATH_TO_PROJECT_DIR
97
- JavaScript file: src/main.ts
345
+
346
+ Supported operators: `eq`, `co`, `sw`, `ew`, `gt`, `ge`, `lt`, `le`, `pr`, `aco`, `and`, `or`.
347
+
348
+ ### Email Providers
349
+
350
+ Pluggable email provider abstraction with support for:
351
+ - **Mailjet** — API-based transactional email
352
+ - **Brevo** — API-based transactional email (formerly Sendinblue)
353
+ - **SMTP** — Standard SMTP via Nodemailer
354
+
355
+ All providers use the same `EmailService` interface with EJS template engine and locale-aware template resolution.
356
+
357
+ ### Extensibility
358
+
359
+ The Module Inheritance Pattern ensures any core module can be extended to integrate with external systems — override service methods to add API calls, event publishing, webhook dispatching, or data synchronization without modifying the framework. See also [Webhook Support](#webhook-support).
360
+
361
+ ## Core Modules
362
+
363
+ | Module | Purpose |
364
+ |--------|---------|
365
+ | **Auth** | Legacy JWT authentication with Passport strategies |
366
+ | **BetterAuth** | Modern auth (2FA, Passkey, Social, sessions, lifecycle hooks) |
367
+ | **User** | User management, profile, roles, verification |
368
+ | **Tenant** | Multi-tenancy with membership and hierarchy roles |
369
+ | **File** | File upload/download with MongoDB GridFS |
370
+ | **Tus** | Resumable file uploads via [tus.io](https://tus.io/) protocol (up to 50 GB) |
371
+ | **ErrorCode** | Centralized error codes with unique identifiers |
372
+ | **HealthCheck** | REST (`/health`) and GraphQL health monitoring |
373
+ | **Migrate** | MongoDB migration system with distributed locking for cluster deployments |
374
+ | **Permissions** | Security audit dashboard (HTML, JSON, Markdown reports) |
375
+ | **SystemSetup** | Initial admin creation for fresh deployments |
376
+
377
+ ### CrudService
378
+
379
+ Abstract base service providing a complete CRUD pipeline with built-in security:
380
+
381
+ ```typescript
382
+ export class ProjectService extends CrudService<Project> {
383
+ // Inherits: find, findOne, create, update, delete
384
+ // With: input validation, field selection, security checks, population
385
+ }
98
386
  ```
99
- see [Debug.run.xml](.run/Debug.run.xml)
100
387
 
101
- ### Debugging as package in a project
102
- Via `pnpm link` the NestJS Server can be linked into the project.
388
+ **Advanced querying** with comparison operators (`eq`, `ne`, `gt`, `in`, `contains`, `regex`, ...), logical operators (`AND`, `OR`), pagination, sorting, and GraphQL-driven population.
103
389
 
104
- In NestJS Server run `pnpm run watch` to watch for changes and rebuild automatically.
105
- Project use following scripts (via `package.json`):
390
+ ### Security Layers
106
391
 
107
- - `pnpm run link:nest-server` (for `pnpm link /path/to/nest-server`)
108
- - `pnpm run unlink:nest-server` (for `pnpm unlink @lenne.tech/nest-server && pnpm install`)
392
+ Defense-in-depth security architecture with three layers:
109
393
 
110
- ## Configuration
394
+ **Layer 1: Guards & Middleware**
395
+ - `@Roles()` — Method-level authorization (includes JWT auth automatically)
396
+ - `@Restricted()` — Field-level access control with process type support (INPUT/OUTPUT)
397
+ - System roles — `S_USER`, `S_VERIFIED`, `S_CREATOR`, `S_SELF`, `S_EVERYONE`, `S_NO_ONE`
111
398
 
112
- The configuration of the server is done via the `src/config.env.ts` file. This file is a TypeScript file that exports
113
- an object with the configuration values. It is automatically integrated into the `ConfigService`
114
- (see src/core/common/services/config.service.ts).
399
+ **Layer 2: CrudService Pipeline**
400
+ - Input validation MapAndValidatePipe with whitelist checking
401
+ - `@UnifiedField()` — Single decorator for GraphQL, Swagger, and validation
402
+ - `securityCheck()` — Resource-level security on model instances
115
403
 
116
- ### Environment variables
404
+ **Layer 3: Mongoose Plugins (Safety Net)**
405
+ - Password Plugin — Automatic BCrypt hashing
406
+ - Role Guard Plugin — Prevents unauthorized role escalation at DB level
407
+ - Audit Fields Plugin — Automatic `createdBy`/`updatedBy` tracking
408
+ - Tenant Isolation Plugin — Automatic tenant filtering
117
409
 
118
- To protect sensitive data and to avoid committing them to the repository the `.env` file can be used.
119
- An example `.env` file is provided in the `.env.example` file.
410
+ **Interceptor Chain:**
411
+ - ResponseModelInterceptor Plain objects to CoreModel conversion
412
+ - TranslateResponseInterceptor — Multi-language support via `Accept-Language`
413
+ - CheckSecurityInterceptor — Executes `securityCheck()` and removes secret fields
414
+ - CheckResponseInterceptor — Enforces `@Restricted()` field-level filtering
415
+
416
+ ### Email & Templates
417
+
418
+ Multi-provider email service (Mailjet, Brevo, SMTP) with EJS template engine and locale-aware template resolution.
419
+
420
+ ## Configuration
120
421
 
121
- There are multiple ways to manipulate or extend the configuration via environment variables:
122
- 1. Via "normal" integration of the environment variables into the `src/config.env.ts`
123
- 2. Via JSON in the `NEST_SERVER_CONFIG` environment variable
124
- 3. Via single environment variables with the prefix `NSC__` (Nest Server Config)
422
+ Configuration is managed via `src/config.env.ts` with environment-based profiles (development, local, production):
125
423
 
126
- #### Normal environment variables
127
- Using `dotenv` (see https://www.dotenv.org/) environment variables can directly integrated into the
128
- `src/config.env.ts` via `process.env`. E.g.:
129
424
  ```typescript
130
425
  export const config = {
131
- development: {
132
- port: process.env.PORT || 3000,
426
+ local: {
427
+ port: 3000,
428
+ graphQl: { driver: 'apollo' },
429
+ mongoose: { uri: 'mongodb://localhost/my-app' },
430
+ betterAuth: { secret: 'my-secret', jwt: true, twoFactor: true },
431
+ multiTenancy: { roleHierarchy: { member: 1, manager: 2, owner: 3 } },
133
432
  },
134
433
  };
135
434
  ```
136
435
 
137
- #### JSON
138
- The `NEST_SERVER_CONFIG` is the environment variable for the server configuration.
139
- The value of `NEST_SERVER_CONFIG` must be a (multiline) JSON string that will be parsed by the server
140
- (see config.env.ts). The keys will override the other configuration values via deep merge
141
- (see https://lodash.com/docs/4.17.15#merge, without array merging).
436
+ ### Configuration Patterns
142
437
 
143
- #### Single config variables
144
- The prefix `NSC__` (**N**est **S**erver **C**onfig) can be used to set single configuration values via environment
145
- variables. The key is the name of the configuration value in uppercase and with double underscores (`__`) instead of
146
- dots. Single underscores are used to separate compound terms like `DEFAULT_SENDER` for `defaultSender`.
147
- For example, the configuration value `email.defaultSender.name` can be set via the environment variable
148
- `NSC__EMAIL_DEFAULT_SENDER_NAME`.
438
+ - **Presence implies enabled**: `rateLimit: {}` enables with defaults, `undefined` stays disabled
439
+ - **Boolean shorthand**: `jwt: true` enables with defaults, `{ expiresIn: '1h' }` customizes
149
440
 
150
- ## Documentation
151
- The API and developer documentation can automatically be generated.
441
+ ### Environment Variables
442
+
443
+ Three methods to override configuration:
444
+
445
+ 1. **Direct** — `process.env.PORT` in `config.env.ts`
446
+ 2. **JSON** — `NEST_SERVER_CONFIG` environment variable (deep merge)
447
+ 3. **Prefixed** — `NSC__EMAIL__DEFAULT_SENDER__NAME` for `email.defaultSender.name`
448
+
449
+ ## Development
450
+
451
+ **Requirements:** Node.js >= 20, MongoDB, pnpm
452
+
453
+ ```bash
454
+ # Install dependencies
455
+ pnpm install
456
+
457
+ # Start in development mode
458
+ pnpm run start:dev
459
+
460
+ # Run tests (Vitest E2E)
461
+ pnpm test
462
+
463
+ # Run tests with coverage
464
+ pnpm run vitest:cov
465
+
466
+ # Lint (oxlint) & format (oxfmt)
467
+ pnpm run lint
468
+ pnpm run format
469
+
470
+ # Build
471
+ pnpm run build
472
+
473
+ # Generate documentation
474
+ pnpm run docs
475
+ ```
476
+
477
+ ### Debugging as package
478
+
479
+ Link into a consuming project for local development:
152
480
 
153
481
  ```bash
154
- # generate and serve documentation
155
- $ pnpm run docs
482
+ # In nest-server
483
+ pnpm run watch
484
+
485
+ # In your project
486
+ pnpm run link:nest-server # pnpm link /path/to/nest-server
487
+ pnpm run unlink:nest-server # pnpm unlink @lenne.tech/nest-server && pnpm install
156
488
  ```
157
489
 
490
+ ### Versioning
491
+
492
+ `MAJOR.MINOR.PATCH` — MAJOR mirrors NestJS version, MINOR = breaking changes, PATCH = non-breaking improvements.
493
+
494
+ ## Documentation
495
+
496
+ - [Request Lifecycle](docs/REQUEST-LIFECYCLE.md) — Complete request pipeline, security architecture, interceptor chain
497
+ - [Migration Guides](migration-guides/) — Version upgrade instructions
498
+ - [Starter Project](https://github.com/lenneTech/nest-server-starter) — Reference implementation
499
+ - [CLI](https://github.com/lenneTech/cli) — Code generation tools
500
+ - [NestJS Documentation](https://docs.nestjs.com/) — Framework docs
501
+
158
502
  ## Thanks
159
503
 
160
- Many thanks to the developers of [Nest](https://github.com/nestjs/nest)
504
+ Many thanks to the developers of [NestJS](https://github.com/nestjs/nest)
161
505
  and all the developers whose packages are used here.
162
506
 
163
507
  ## License
164
508
 
165
- MIT - see LICENSE
509
+ MIT - see [LICENSE](/LICENSE)
@@ -12,6 +12,7 @@ export declare const checkRestricted: (data: any, user: {
12
12
  emailVerified?: any;
13
13
  hasRole: (roles: string[]) => boolean;
14
14
  id: any;
15
+ roles?: string[];
15
16
  verified?: any;
16
17
  verifiedAt?: any;
17
18
  }, options?: {
@@ -6,6 +6,8 @@ require("reflect-metadata");
6
6
  const _ = require("lodash");
7
7
  const role_enum_1 = require("../enums/role.enum");
8
8
  const db_helper_1 = require("../helpers/db.helper");
9
+ const request_context_service_1 = require("../services/request-context.service");
10
+ const core_tenant_helpers_1 = require("../../modules/tenant/core-tenant.helpers");
9
11
  const restrictedMetaKey = Symbol('restricted');
10
12
  const Restricted = (...rolesOrMember) => {
11
13
  return Reflect.metadata(restrictedMetaKey, rolesOrMember);
@@ -85,7 +87,8 @@ const checkRestricted = (data, user, options = {}, processedObjects = []) => {
85
87
  (roles.includes(role_enum_1.RoleEnum.S_CREATOR) &&
86
88
  (('createdBy' in data && (0, db_helper_1.equalIds)(data.createdBy, user)) ||
87
89
  (config.allowCreatorOfParent && !('createdBy' in data) && config.isCreatorOfParent))) ||
88
- (roles.includes(role_enum_1.RoleEnum.S_VERIFIED) && (user?.verified || user?.verifiedAt || user?.emailVerified))) {
90
+ (roles.includes(role_enum_1.RoleEnum.S_VERIFIED) && (user?.verified || user?.verifiedAt || user?.emailVerified)) ||
91
+ (user?.id && (0, core_tenant_helpers_1.checkRoleAccess)(roles, user?.roles, request_context_service_1.RequestContext.get()?.tenantRole))) {
89
92
  valid = true;
90
93
  }
91
94
  }