# Migration Guide: 11.22.x → 11.23.0

## Overview

| Category | Details |
|----------|---------|
| **Breaking Changes** | `mainDbModel.collection` and `mainDbModel.db` blocked via TypeScript (`SafeModel`) |
| **New Features** | `process()` depth-based optimization, `debugProcessInput` config, `getNativeCollection(reason)`, `getNativeConnection(reason)`, restricted metadata cache |
| **Performance** | ~70% less memory on nested service cascades, eliminated redundant `JSON.stringify` and recursive `process()` calls |
| **Migration Effort** | ~5 minutes if using `mainDbModel.db`; 0 minutes otherwise |

---

## Quick Migration (No `mainDbModel.db` Usage)

If your project does **not** access `this.mainDbModel.collection` or `this.mainDbModel.db` directly:

```bash
# Update package
pnpm add @lenne.tech/nest-server@11.23.0

# Verify build
pnpm run build

# Run tests
pnpm test
```

No code changes required. All performance optimizations activate automatically.

---

## Breaking Changes

### `mainDbModel` Type Changed to `SafeModel` (TypeScript Only)

`mainDbModel` is now typed as `SafeModel<T>` which is `Omit<Model<T>, 'collection' | 'db'>`. This is a **compile-time-only** change — runtime behavior is identical.

**What breaks:** Direct access to `.collection` or `.db` on `this.mainDbModel` produces a TypeScript error.

**What does NOT break:** All Mongoose Model methods (`find`, `findById`, `insertMany`, `updateOne`, `aggregate`, `bulkWrite`, etc.) continue to work unchanged.

#### If You Use `mainDbModel.collection`

**Before:**
```typescript
// Direct native collection access — bypasses ALL Mongoose plugins
await this.mainDbModel.collection.insertOne(doc);
```

**After (Option A — use Mongoose Model method):**
```typescript
// Mongoose method — plugins (Tenant, Audit, RoleGuard, Password) fire correctly
await this.mainDbModel.insertMany([doc]);
```

**After (Option B — intentional native access with logging):**
```typescript
// Logged escape hatch — requires justification
const col = this.getNativeCollection('Migration: bulk-import historical data without tenant context');
await col.insertOne(doc);
```

#### If You Use `mainDbModel.db`

**Before:**
```typescript
// Native DB access via model — bypasses ALL Mongoose plugins
const count = await this.mainDbModel.db.db.collection('chatmessages').countDocuments({ ... });
```

**After (Option A — inject the target model):**
```typescript
// Inject the model via @InjectModel and use Mongoose methods
constructor(@InjectModel('ChatMessage') private chatMessageModel: Model<ChatMessageDocument>) {}

const count = await this.chatMessageModel.countDocuments({ ... });
```

**After (Option B — intentional native access with logging):**
```typescript
// Logged escape hatch — requires justification
const conn = this.getNativeConnection('Statistics: count chatmessages across all tenants');
const count = await conn.db.collection('chatmessages').countDocuments({ ... });
```

#### If You Use `getModel()`

`getModel()` continues to return the full `Model` type (including `.collection` and `.db`). No change needed.

---

## What's New in 11.23.0

### 1. process() Pipeline Performance Optimization

The `process()` method in `ModuleService` now tracks nesting depth via `RequestContext`. On nested calls (service cascades like A.create → B.create → C.create), redundant pipeline steps are skipped:

| Step | Outermost Call | Nested Calls |
|------|---------------|--------------|
| prepareInput | Runs | Runs |
| checkRights (input) | Runs | Runs |
| serviceFunc | Runs | Runs |
| processFieldSelection (populate) | Runs | **Skipped** (unless explicit `populate` is set) |
| prepareOutput (model mapping) | Runs | **Skipped** (secret removal still active) |
| checkRights (output) | Runs | **Skipped** |

**Security is maintained** through three layers:
1. Input checkRights always runs at every depth
2. Output checkRights runs at depth 0 (outermost call)
3. `CheckSecurityInterceptor` (Safety Net) runs on the final HTTP response

**Estimated savings for an 8-level service cascade:** ~70% less memory (~120 KB instead of ~400 KB).

No configuration needed — this activates automatically.

### 2. Lean Query for Rights Checking

The `process()` pipeline previously called `this.get()` to resolve `dbObject` for authorization checks, which triggered a **recursive** `process()` call. It now uses a direct lean query:

```typescript
// Before: recursive process() call with full pipeline
const dbObject = await this.get(id);

// After: single lean query + map
const rawDoc = await this.mainDbModel.findById(id).lean().exec();
const dbObject = mainModelConstructor.map(rawDoc);
```

This preserves ALL fields (including `createdBy`) needed for `S_CREATOR` and `S_SELF` checks, while avoiding the overhead of a full pipeline pass.

### 3. `debugProcessInput` Configuration Option

The previous `JSON.stringify` debug comparison that ran on **every** `process()` call is now behind a config flag:

```typescript
// config.env.ts — enable only for debugging
{
  debugProcessInput: true,  // default: false
}
```

When disabled (default), two `JSON.stringify` calls per `process()` invocation are eliminated.

### 4. Restricted Metadata Cache

`getRestricted()` results (from `@Restricted()` decorators) are now cached per class + property. Since decorator metadata is static (set at compile time and never changes), this eliminates hundreds of `Reflect.getMetadata()` lookups per request for objects with many properties.

### 5. Native Driver Security (`SafeModel`, `getNativeCollection`, `getNativeConnection`)

Two new protected methods in `CrudService` provide logged escape hatches for legitimate native MongoDB driver access:

```typescript
// Get the native MongoDB Collection for this model's collection
protected getNativeCollection(reason: string): Collection

// Get the Mongoose Connection (for cross-collection access, schema-less collections)
protected getNativeConnection(reason: string): Connection
```

Both methods:
- Require a non-empty justification string
- Log a `[SECURITY]` warning on every call
- Throw an `Error` if no reason is provided

The `SafeModel<T>` type is exported for use in custom service implementations.

### 6. RequestContext.processDepth API

New methods for tracking `process()` nesting depth:

```typescript
// Get current depth (0 = outermost or no process() call)
RequestContext.getProcessDepth(): number

// Run a function with incremented depth
RequestContext.runWithIncrementedProcessDepth<T>(fn: () => T): T
```

These are used internally by `process()` but are also available for custom pipeline implementations.

---

## Compatibility Notes

| Pattern | Status |
|---------|--------|
| All `CoreModule.forRoot()` patterns | Works unchanged |
| `CrudService` subclasses | Works unchanged (SafeModel is transparent for Mongoose methods) |
| Custom `process()` calls with `populate` option | Works unchanged (explicit populate overrides nested skip) |
| `getModel()` callers | Works unchanged (returns full Model type) |
| `@Restricted()` decorators | Works unchanged (cache is transparent) |
| Services using `this.mainDbModel.find/save/update/etc.` | Works unchanged |
| Services using `this.mainDbModel.collection` | **TypeScript error** — use `getNativeCollection(reason)` or Mongoose methods |
| Services using `this.mainDbModel.db` | **TypeScript error** — use `getNativeConnection(reason)` or inject target model |
| Services using `@InjectConnection` | Works unchanged (Connection is not affected by SafeModel) |

---

## Troubleshooting

| Issue | Solution |
|-------|----------|
| TypeScript error on `mainDbModel.collection` | Replace with Mongoose Model method or `this.getNativeCollection('reason')` |
| TypeScript error on `mainDbModel.db` | Replace with `this.getNativeConnection('reason')` or inject the target model via `@InjectModel` |
| `getModel()` return type mismatch | `getModel()` returns full `Model` — no change needed |
| `processFieldSelection` type error in custom override | Update parameter type to accept `SafeModel` (see `ModuleService.processFieldSelection`) |
| Debug logs from `prepareInput` disappeared | Set `debugProcessInput: true` in config to re-enable |

---

## Module Documentation

### Native Driver Security

- **Security Policy:** [`docs/native-driver-security.md`](../docs/native-driver-security.md)
- **Performance Optimization:** [`docs/process-performance-optimization.md`](../docs/process-performance-optimization.md)

### Affected Source Files

| File | Changes |
|------|---------|
| `src/core/common/services/module.service.ts` | `SafeModel` type, `mainDbModel` type change, `process()` depth optimization, lean query, debug config |
| `src/core/common/services/crud.service.ts` | `getNativeCollection(reason)`, `getNativeConnection(reason)`, `getModel()` cast |
| `src/core/common/services/request-context.service.ts` | `processDepth` field, `getProcessDepth()`, `runWithIncrementedProcessDepth()` |
| `src/core/common/decorators/restricted.decorator.ts` | Metadata cache, `_.uniq()` optimization |
| `src/core/common/interfaces/server-options.interface.ts` | `debugProcessInput` config option |

---

## References

- [Native Driver Security Policy](../docs/native-driver-security.md)
- [process() Performance Optimization](../docs/process-performance-optimization.md)
- [nest-server-starter](https://github.com/lenneTech/nest-server-starter) (reference implementation)
