NestJS: проектирование REST API для SaaS

NestJS: проектирование REST API для SaaS

NestJS задаёт структуру, но не спасает от «god service» и inconsistent API. Ниже — практики, которые масштабируются от MVP до десятков модулей.

NestJS: проектирование REST API для SaaS
Иллюстрация к материалу

Границы модулей

Один bounded context = один Nest module (shop, auth, courses). Shared — только utils без бизнес-логики.

  • Controller — HTTP, Service — use cases, Repository/Model — persistence
  • Cross-module — через публичный service или events, не import private files

DTO и валидация

  • class-validator на все входящие body/query
  • Единый формат ошибок { statusCode, message, errors[] }
  • Whitelist + forbidNonWhitelisted против mass assignment

Auth и multi-tenant

  • JwtAuthGuard + RolesGuard + OptionalJwt для guest checkout
  • tenantId из JWT, не из body
  • Admin routes под отдельным prefix /admin
NestJS: проектирование REST API для SaaS
Схема и рабочий процесс

Пагинация и фильтры

Cursor pagination для лент; offset — только для админок с малым объёмом.

Чеклист

  • OpenAPI/Swagger актуален
  • Idempotency-Key на payment endpoints
  • Health + readiness probes
  • Versioning /api/v1 при breaking changes

Структура ошибок API

Клиенту всегда возвращайте предсказуемый JSON: код, человекочитаемое сообщение, массив field errors для форм.

Внутренние stack trace не утекают наружу; correlation id (traceId) пишется в лог и отдаётся клиенту для support.

Версионирование без боли

Breaking changes — только через /v2 или заголовок Accept-Version. Старый контракт живёт минимум один релизный цикл с deprecation warning в OpenAPI.

Тестирование контракта

E2e на critical paths (auth, checkout, webhook) + contract tests для mobile/BFF. При изменении DTO — обновляйте Swagger и consumer tests в одном PR, иначе фронт и партнёры ломаются «тихо».

Interceptor и logging

Global interceptor добавляет requestId, duration, userId в лог. Exception filter мапит domain errors (InsufficientFunds) в 402/409, не в generic 500.

Для admin endpoints — отдельный rate limit и audit log: кто менял site_settings, кто выдал refund. Это дешевле, чем расследование без trail.

Как разобраться с «god service»

  • Нарисуйте граф import между модулями — циклы видны сразу
  • Вынесите shared logic в domain service с явным public API
  • Один модуль за спринт: controller остаётся, logic уезжает
  • Добавьте e2e на один critical flow после каждого split