7.3 KiB
7.3 KiB
Cross-Cutting Concerns
Aspects that span all phases and must be maintained consistently throughout development.
API Versioning
- All endpoints prefixed with
/api/v1/ - Version is part of the URL, not headers
- When breaking changes are needed, introduce
/api/v2/alongside v1 - Deprecation: v1 endpoints log warnings 3 months before removal
Pagination
All list endpoints use cursor-based pagination:
// Request
GET /api/v1/products?cursor=abc123&limit=20
// Response
{
"data": [...],
"pagination": {
"cursor": "def456", // Pass as next cursor, null = no more pages
"hasMore": true,
"total": 150 // Total count (optional, can be expensive)
}
}
- Default
limit: 20, max: 100 - Cursor is an opaque string (encoded
_idor composite sort key) - Prefer cursor over offset/skip for MongoDB performance
Audit Trail
Every domain document includes:
{
createdAt: Date; // Set on creation, never modified
updatedAt: Date; // Updated on every modification
createdBy: string; // userId who created
}
For sensitive operations (deletes, status transitions, admin actions), maintain an AuditLog collection:
interface AuditLog {
id: string;
householdId: string;
userId: string;
action: string; // 'product.delete', 'pantry.transition', etc.
entityType: string; // 'Product', 'PantryItem', etc.
entityId: string;
changes?: Record<string, { from: any; to: any }>;
timestamp: Date;
}
Implemented as a NestJS interceptor that logs after successful mutations.
Error Handling
Consistent error response format across all endpoints:
interface ApiError {
statusCode: number;
error: string; // HTTP status text
message: string; // Human-readable message
details?: any; // Validation errors, etc.
timestamp: string;
path: string;
}
Global exception filter in NestJS catches:
ValidationException→ 400UnauthorizedException→ 401ForbiddenException→ 403NotFoundException→ 404ConflictException→ 409 (e.g., duplicate barcode)TooManyRequestsException→ 429 (LLM rate limit)- Unhandled errors → 500 (log stack trace, return generic message)
Real-Time (WebSocket)
NestJS @WebSocketGateway with Socket.IO:
- Authentication: validate JWT on connection
- Rooms: one room per
householdId— all household members receive household events - Namespaces: optional per-feature namespaces (
/pantry,/shopping)
Events introduced per phase:
| Phase | Events |
|---|---|
| 3 | pantry:item-added, pantry:item-updated, pantry:freshness-alert |
| 5 | shopping:item-checked, shopping:item-added, shopping:item-removed, shopping:list-updated |
| 4 | meal-plan:updated |
Frontend pattern:
- Connect on app mount, join household room
- Use React context/Zustand store to distribute events to components
- Optimistic UI updates with server reconciliation
Testing Strategy
Unit Tests (per module, per phase)
- Services: business logic, calculations (nutrition calculator, freshness calculator, suggestion scoring)
- Guards/Interceptors: auth, household scoping
- Mocking: MongoDB operations mocked for service tests
- Framework: Jest
Integration Tests (per module)
- In-memory MongoDB (
mongodb-memory-server) or test containers - Test full request → service → database → response cycle
- Framework: Supertest + Jest
E2E Tests (per phase milestone)
- Full Docker Compose stack
- Automated script that exercises the happy path:
- Register/login user
- Create household
- Add products
- Create recipe
- Stock pantry
- Generate meal plan
- Create shopping list
- Check off items → add to pantry
- Framework: Supertest or Playwright (for web UI)
Frontend Tests
- Component tests: React Testing Library
- Hook tests:
@testing-library/react-hooks - E2E: Playwright for critical flows (login, add product, create recipe)
Test Coverage Targets
| Type | Target |
|---|---|
| Unit | 80%+ |
| Integration | Key flows covered |
| E2E | Happy path per phase |
Mobile Readiness
Design decisions to facilitate React Native development later:
- All business logic in API: the web frontend is a thin UI layer. Mobile will consume the same API.
- Shared types package:
packages/sharedis platform-agnostic TypeScript. Mobile imports it directly. - No SSR dependencies in shared code: avoid Next.js-specific imports in
packages/shared. - API client layer:
packages/web/src/services/api.tswraps fetch/axios with auth. Mobile will have its own but same pattern. - WebSocket events: same events work on mobile (Socket.IO has React Native support).
- Auth: Keycloak has React Native OIDC libraries (
react-native-app-auth). - Image handling: API accepts standard multipart uploads — works from any client.
- Push notifications: Phase 3 starts with in-app notifications. Mobile phase adds Firebase Cloud Messaging (FCM) as a notification channel.
When the mobile phase begins:
- Add
packages/mobile(React Native via Expo or bare workflow) - Share from
packages/shared - Build mobile-optimized UI for key flows: pantry check, shopping list, quick add
Security
- Authentication: All API routes require valid Keycloak JWT (except health check)
- Authorization: Household-scoped — users can only access data for their households
- Input validation: Zod schemas validate all inputs; NestJS validation pipe rejects invalid requests
- Rate limiting: per-IP and per-household (configurable)
- CORS: restricted to known origins
- Helmet: HTTP security headers via
@nestjs/helmet - Secrets: environment variables, never committed;
.env.exampledocuments required vars - Image uploads: validate file type and size; store in local volume or S3-compatible storage
- MongoDB: authenticated access, least-privilege user for the app
Performance
- MongoDB indexes: reviewed and optimized per phase (documented in each phase doc)
- Query profiling: enable MongoDB slow query log in dev; review before phase sign-off
- Caching: consider Redis for:
- Product search results (short TTL)
- Freshness rule lookups (rarely change)
- LLM response caching (identical inputs)
- Denormalization: nutrition on recipes, product names on pantry items — reduces lookups
- Pagination: cursor-based, no
skip()for large collections - Compression: gzip responses via NestJS middleware
Observability (Future)
Not in initial phases, but plan for:
- Structured logging: Pino or Winston with JSON format
- Health checks:
/api/v1/healthreturns status of MongoDB, Keycloak connectivity - Metrics: Prometheus-compatible endpoint (NestJS has plugins)
- Tracing: OpenTelemetry for request tracing across services
- Error tracking: Sentry integration (optional, self-hosted instance possible)