# Error Handling Integration Guide

Quick start guide for integrating standardized error handling into each backend.

## Sprint 1 P1 - Cross-cutting Corrections

### Projects to Update

1. **gamilit** (NestJS) - `/home/isem/workspace/projects/gamilit/apps/backend`
2. **trading-platform** (Express) - `/home/isem/workspace/projects/trading-platform/apps/backend`
3. **platform_marketing_content** (NestJS) - `/home/isem/workspace/projects/platform_marketing_content/apps/backend`

---

## NestJS Integration (gamilit, platform_marketing_content)

### Step 1: Update main.ts

```typescript
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { GlobalExceptionFilter } from '@erp-suite/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Add global exception filter
  app.useGlobalFilters(new GlobalExceptionFilter());

  // ... rest of your configuration

  await app.listen(3000);
}

bootstrap();
```

### Step 2: Update Services

Replace manual error throwing with standardized errors:

**Before:**
```typescript
async findById(id: string): Promise<User> {
  const user = await this.repository.findOne({ where: { id } });
  if (!user) {
    throw new HttpException('User not found', HttpStatus.NOT_FOUND);
  }
  return user;
}
```

**After:**
```typescript
import { NotFoundError } from '@erp-suite/core';

async findById(id: string): Promise<User> {
  const user = await this.repository.findOne({ where: { id } });
  if (!user) {
    throw new NotFoundError('User not found', { userId: id });
  }
  return user;
}
```

### Step 3: Remove Try/Catch from Controllers

**Before:**
```typescript
@Get(':id')
async getUser(@Param('id') id: string) {
  try {
    return await this.service.findById(id);
  } catch (error) {
    throw new HttpException('Error finding user', 500);
  }
}
```

**After:**
```typescript
@Get(':id')
async getUser(@Param('id') id: string) {
  return this.service.findById(id);
  // GlobalExceptionFilter handles errors automatically
}
```

### Step 4: Add Request ID Middleware (Optional)

```typescript
// src/middleware/request-id.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { randomUUID } from 'crypto';

@Injectable()
export class RequestIdMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const requestId =
      (req.headers['x-request-id'] as string) ||
      randomUUID();

    req.headers['x-request-id'] = requestId;
    res.setHeader('X-Request-ID', requestId);

    next();
  }
}
```

```typescript
// src/app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { RequestIdMiddleware } from './middleware/request-id.middleware';

@Module({
  // ... your module config
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(RequestIdMiddleware).forRoutes('*');
  }
}
```

---

## Express Integration (trading-platform)

### Step 1: Update Main Server File

```typescript
// src/index.ts
import express from 'express';
import {
  createErrorMiddleware,
  notFoundMiddleware
} from '@erp-suite/core';

const app = express();

// Body parsing
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Request ID middleware (optional)
app.use(requestIdMiddleware);

// Your routes
app.use('/api/users', usersRouter);
app.use('/api/products', productsRouter);

// 404 handler (must be before error handler)
app.use(notFoundMiddleware);

// Error handling middleware (MUST BE LAST)
app.use(createErrorMiddleware({
  includeStackTrace: process.env.NODE_ENV !== 'production'
}));

app.listen(3000);
```

### Step 2: Update Route Handlers

**Before:**
```typescript
router.get('/:id', async (req, res) => {
  try {
    const user = await findUser(req.params.id);
    res.json(user);
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' });
  }
});
```

**After:**
```typescript
import { NotFoundError } from '@erp-suite/core';

// Create async handler helper
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

router.get('/:id', asyncHandler(async (req, res) => {
  const user = await findUser(req.params.id);
  res.json(user);
  // Errors automatically caught and passed to error middleware
}));

// In service/model
async function findUser(id: string): Promise<User> {
  const user = await db.users.findOne(id);
  if (!user) {
    throw new NotFoundError('User not found', { userId: id });
  }
  return user;
}
```

### Step 3: Add Request ID Middleware (Optional)

```typescript
// src/middleware/request-id.middleware.ts
import { Request, Response, NextFunction } from 'express';
import { randomUUID } from 'crypto';

export function requestIdMiddleware(
  req: Request,
  res: Response,
  next: NextFunction
) {
  const requestId =
    (req.headers['x-request-id'] as string) ||
    randomUUID();

  (req as any).requestId = requestId;
  res.setHeader('X-Request-ID', requestId);

  next();
}
```

### Step 4: Update Services

```typescript
// Before
async function createUser(email: string, name: string) {
  if (!email.includes('@')) {
    throw new Error('Invalid email');
  }
  // ...
}

// After
import { ValidationError, ConflictError } from '@erp-suite/core';

async function createUser(email: string, name: string) {
  if (!email.includes('@')) {
    throw new ValidationError('Invalid email format', {
      field: 'email',
      value: email
    });
  }

  const existing = await db.users.findByEmail(email);
  if (existing) {
    throw new ConflictError('Email already exists', {
      email,
      existingUserId: existing.id
    });
  }

  // ...
}
```

---

## Common Error Patterns

### Validation Errors

```typescript
import { ValidationError } from '@erp-suite/core';

throw new ValidationError('Validation failed', {
  errors: [
    { field: 'email', message: 'Invalid format' },
    { field: 'age', message: 'Must be at least 18' }
  ]
});
```

### Not Found

```typescript
import { NotFoundError } from '@erp-suite/core';

throw new NotFoundError('Resource not found', {
  resourceType: 'User',
  resourceId: id
});
```

### Unauthorized

```typescript
import { UnauthorizedError } from '@erp-suite/core';

throw new UnauthorizedError('Invalid credentials');
```

### Conflict

```typescript
import { ConflictError } from '@erp-suite/core';

throw new ConflictError('Email already exists', {
  email,
  existingId: existingUser.id
});
```

### Custom Domain Errors

```typescript
import { BaseError } from '@erp-suite/core';

export class InsufficientBalanceError extends BaseError {
  readonly statusCode = 400;
  readonly error = 'Insufficient Balance';

  constructor(required: number, available: number) {
    super('Insufficient balance for this operation', {
      required,
      available,
      deficit: required - available
    });
  }
}
```

---

## Testing

### NestJS Tests

```typescript
it('should return 404 when user not found', async () => {
  const response = await request(app.getHttpServer())
    .get('/users/999')
    .expect(404);

  expect(response.body).toMatchObject({
    statusCode: 404,
    error: 'Not Found',
    message: 'User not found'
  });
});
```

### Express Tests

```typescript
it('should return 404 when user not found', async () => {
  const response = await request(app)
    .get('/api/users/999')
    .expect(404);

  expect(response.body).toMatchObject({
    statusCode: 404,
    error: 'Not Found',
    message: 'User not found'
  });
});
```

---

## Migration Checklist

### For Each Backend:

- [ ] Install/update `@erp-suite/core` dependency
- [ ] Add global error filter/middleware
- [ ] Add request ID middleware (optional but recommended)
- [ ] Update services to use standardized error classes
- [ ] Remove try/catch blocks from controllers/routes
- [ ] Update error responses in tests
- [ ] Test error responses in development
- [ ] Verify error logging works correctly
- [ ] Update API documentation with new error format

---

## Error Response Format

All backends now return errors in this standardized format:

```json
{
  "statusCode": 404,
  "error": "Not Found",
  "message": "User not found",
  "details": {
    "userId": "999"
  },
  "timestamp": "2025-12-12T10:30:00.000Z",
  "path": "/api/users/999",
  "requestId": "550e8400-e29b-41d4-a716-446655440000"
}
```

---

## Available Error Classes

| Class | Status | Use Case |
|-------|--------|----------|
| `BadRequestError` | 400 | Invalid request parameters |
| `UnauthorizedError` | 401 | Missing/invalid authentication |
| `ForbiddenError` | 403 | Insufficient permissions |
| `NotFoundError` | 404 | Resource doesn't exist |
| `ConflictError` | 409 | Resource conflict |
| `ValidationError` | 422 | Validation failed |
| `InternalServerError` | 500 | Unexpected server error |

---

## Support

For detailed examples, see:
- `nestjs-integration.example.ts`
- `express-integration.example.ts`
- `README.md`
