---
title: Error Handling Patterns
impact: HIGH
impactDescription: Proper error handling ensures debuggable and secure code
tags: errors, nestjs, services, exceptions
---
## Паспорт документа

- Статус документа: living standard
- Актуально на: 28 марта 2026 года
- Владелец: backend/platform-команда
- Пересмотр: при изменении инженерной практики, CI/CD, архитектурных правил или локального workflow
- Область применения: внутренние rule/reference-card документы для инженерной команды
- Связанные документы:
  - [Индекс Agents](../README.md)
  - [Команды разработки](../commands.md)
  - [Инженерные принципы](../../governance/engineering-principles.md)

## Error Handling Patterns

**Impact: HIGH**

### Descriptive Errors

Always include context in error messages — IDs, slugs, seller references — so logs are actionable.

### Error Classes by Layer

Use the right exception class depending on where you are in the code.

#### In Controllers (HTTP layer) — NestJS exceptions

```typescript
import { NotFoundException, BadRequestException, ForbiddenException } from '@nestjs/common';

@Get(':slug')
async getBySlug(@Param('slug') slug: string) {
  const item = await this.itemService.findBySlug(slug);
  if (!item) throw new NotFoundException(`Item with slug "${slug}" not found`);
  return item;
}
```

#### In Services (business logic) — Domain exceptions

```typescript
import { DomainException, ErrorCode } from '@repo/shared';

@Injectable()
export class ItemService {
  async publish(itemId: string, sellerId: string) {
    const item = await this.itemRepo.findById(itemId);
    if (!item) {
      throw new DomainException(ErrorCode.ITEM_NOT_FOUND, `Item ${itemId} not found`);
    }
    if (item.sellerId !== sellerId) {
      throw new DomainException(ErrorCode.FORBIDDEN, `Seller ${sellerId} does not own item ${itemId}`);
    }
  }
}
```

#### Global Exception Filter

NestJS global exception filter converts `DomainException` to proper HTTP responses automatically. Services don't need to know about HTTP status codes.

```typescript
// common/filters/domain-exception.filter.ts
@Catch(DomainException)
export class DomainExceptionFilter implements ExceptionFilter {
  catch(exception: DomainException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const status = this.mapErrorCodeToStatus(exception.code);

    response.status(status).send({
      statusCode: status,
      errorCode: exception.code,
      message: exception.message,
    });
  }
}
```

### Rules
- **Controllers**: Use `@nestjs/common` exceptions (`NotFoundException`, `BadRequestException`, etc.)
- **Services**: Use `DomainException` with `ErrorCode` enum from `@repo/shared`
- **Repositories**: Let Prisma errors bubble up — the service handles them
- **Never**: Use plain `throw new Error()` in services — always use typed exceptions
