AquaX API Architecture
Owner: Backend & Architecture
Reviewers: Web, Mobile, QA, Security, DevOps
Status: Draft - Current-Code Baseline
Version: 0.1
Last Updated: 2026-09-17
Review Cycle: Per API runtime, module boundary, auth, integration or deployment change
1. Purpose And Scope
This document describes the current AquaX API architecture as implemented in the repository. It focuses on runtime structure, module boundaries, request flow, authentication, authorization, validation, response conventions, data access, client integration, IoT integration and operational risks.
This document is different from docs/08_API Specification.md:
08_API Specification.mdlists API contracts and endpoint inventory.API_Architecture.mdexplains how the API is designed, bootstrapped, secured, layered and integrated.
Sources reviewed:
backend/main.tsbackend/app.module.tsbackend/common/**backend/modules/**web/src/core/config/axios.config.tsweb/src/core/constants/api-url.tsmobile/src/core/config/api-client.tsmobile/src/core/constants/apiURL.constants.tsdocs/08_API Specification.mddocs/current/architecture/backend-architecture.md
2. High-Level API Context
AquaX backend is a NestJS REST API. It is the server-side source of truth for:
- authentication and session management;
- user, role, farm and pond scope;
- farm, pond and crop operations;
- water quality, sensors, thresholds and telemetry;
- devices, device commands and auto-rules;
- alerts, tickets, notifications and activity logs;
- feeding records, farming logs and reports;
- handbook and locations;
- dashboard and settings.
Current global API prefix:
/api
Runtime Swagger documentation:
/api/docs
Default local port:
3001
3. Runtime Bootstrap
backend/main.ts creates the Nest application and configures API-wide concerns.
| Concern | Current implementation |
|---|---|
| Framework | NestJS on Express adapter |
| Global prefix | app.setGlobalPrefix("api") |
| Validation | Global ValidationPipe with whitelist, forbidNonWhitelisted, transform |
| CORS | Configured from CORS_ORIGINS, with localhost defaults |
| Credentials | CORS credentials enabled |
| Allowed methods | GET, POST, PUT, PATCH, DELETE |
| Allowed headers | Content-Type, Authorization, x-client-type, x-refresh-token |
| Cookie parsing | Lightweight parser for req.cookies, used by refresh token flow |
| Swagger | SwaggerModule.setup("api/docs", ...) |
| Request logging | Successful /api requests logged except Swagger assets |
| Port | PORT env or 3001 |
Runtime flow:
HTTP client
-> CORS and cookie parsing
-> /api global prefix
-> validation pipe
-> throttler guard
-> controller
-> use case/service
-> repository/Prisma/GCS/cache/email/MQTT
-> response DTO wrapper
4. Application Module Composition
backend/app.module.ts wires global infrastructure and feature modules.
Global infrastructure:
| Item | Current code |
|---|---|
| Config | ConfigModule.forRoot({ isGlobal: true, envFilePath: ".env" }) |
| Rate limit | ThrottlerModule global default 100 requests per minute per IP |
| Scheduling | ScheduleModule.forRoot() |
| Common services | CommonModule |
| Global guard | ThrottlerGuard |
| Global filter | PrismaExceptionFilter |
Imported feature modules:
| Module | Responsibility |
|---|---|
AuthModule |
Login, logout, refresh, password reset, profile, sessions |
UsersModule |
User CRUD, scope assignment, audit logs, technician assignment |
FarmsModule |
Farm CRUD, owner/viewer/technician views, farm dashboard |
PondsModule |
Pond CRUD, assignments, water/device/feeding/log/warning/report/dashboard tabs |
CropsModule |
Crop lifecycle, species, size ranges |
SensorsModule |
Sensor CRUD, readings and chart data |
ParameterThresholdsModule |
Pond/environment threshold configuration |
HealthModule |
Health and status routes |
AlertsModule |
Alert lifecycle and read state |
DevicesModule |
Device CRUD, commands, auto-rules |
FarmingLogsModule |
Water, mineral, siphon, productivity and attachments |
FeedingModule |
Feed types, feeding records, suggestion, PCR/FCR |
TicketsModule |
Ticket lifecycle, attachment and comment flow |
NotificationsModule |
Notifications, device tokens and notification config |
HandbookModule |
Articles, versions, bookmarks and approval workflow |
ReportsModule |
Operational reports and Excel export jobs |
LocationsModule |
Provinces and wards |
IotIngestModule |
IoT telemetry, command ACK and device registration controls |
SettingsModule |
Incident response settings |
ActivityLogsModule |
Activity listing and export |
TodoTasksModule |
Mobile/user task list |
DashboardModule |
Owner/viewer dashboard APIs |
5. Common Infrastructure Layer
CommonModule is marked @Global() and exports shared providers.
| Provider | Token/class | Behavior |
|---|---|---|
| Database | PrismaService |
Prisma Client access to PostgreSQL/TimescaleDB schema |
| File storage | GcsService |
Google Cloud Storage integration for uploads/attachments/reports |
| Cache | CACHE_SERVICE_TOKEN |
Uses Redis when REDIS_HOST exists; otherwise falls back to in-memory cache |
EMAIL_SERVICE_TOKEN |
Uses Gmail SMTP when EMAIL_PROVIDER=gmail; otherwise console email |
Architectural rule:
- Feature modules should depend on the exported CommonModule providers instead of creating direct infrastructure clients ad hoc.
- New email providers, cache providers or storage providers should be added through CommonModule/provider tokens.
6. Layering Pattern
The codebase uses a pragmatic NestJS module architecture. Not every module has identical internal folders, but the dominant pattern is:
controller
-> use-case/service
-> repository
-> PrismaService / external provider
Common module folders:
backend/modules/<module>/
dto/
entities/
repositories/
services/
use-cases/
<module>.controller.ts
<module>.module.ts
Implementation note:
- Some mature modules use repositories and use cases.
- Some modules still use direct service methods.
- New work should follow the local pattern inside the module being changed instead of forcing a repo-wide refactor.
7. API Surface Map
All paths below are mounted under /api.
| Area | Base route(s) | Architecture role |
|---|---|---|
| Health | /health, /status.json |
Runtime health checks |
| Auth | /auth |
Identity, session and password lifecycle |
| Users | /users |
User management, scope, KTV assignment |
| Farms | /farms |
Farm management and role-specific farm views |
| Ponds | /ponds |
Pond management, assignment and pond operational tabs |
| Crops | /crops |
Crop lifecycle and catalog data |
| Sensors | /sensors |
Sensor metadata and sensor readings |
| Parameter thresholds | /parameter-thresholds |
Environment threshold configuration |
| Devices | /devices |
Device CRUD, command logs and auto-rules |
| IoT ingest | /iot/v1 |
Device telemetry, command ACK, registration and control |
| Alerts | /alerts |
Alert workflow |
| Tickets | /tickets |
Issue/ticket workflow |
| Notifications | /notifications, /notifications/config |
Notification inbox, tokens and config |
| Farming logs | /farming-logs |
Manual water, mineral, siphon, productivity and attachment logs |
| Feeding | /feeding-records |
Feed types, feeding records and calculated metrics |
| Reports | /reports |
Report views and Excel jobs |
| Handbook | /handbook |
Knowledge base and article approval lifecycle |
| Locations | /locations |
Provinces and wards |
| Settings | /settings |
Operational settings |
| Activity logs | /activity-logs |
Activity audit view/export |
| Todo tasks | /todo-tasks |
Task list APIs |
| Dashboard | /dashboard |
Owner/viewer dashboard aggregation |
For endpoint-level detail, use docs/08_API Specification.md or runtime Swagger at /api/docs.
8. Authentication Architecture
8.1 Access Token Flow
Protected API routes use bearer access tokens:
Authorization: Bearer <accessToken>
JwtAuthGuard:
- Extracts the bearer token.
- Verifies token signature and expiry through
JwtTokenService. - Checks account active state from token payload.
- Validates session state through
SessionService. - Updates session activity.
- Attaches
request.user.
Attached user shape:
id
email
roles
farmIds
pondIds
sessionId
8.2 Refresh Token Flow
Web:
- Login sets refresh token in an httpOnly cookie named
refreshToken. /auth/refreshreads the cookie.
Mobile:
- Mobile sends
x-client-type: mobile. - Login/refresh responses can include
refreshTokenin JSON. - Refresh can send token in body or
x-refresh-tokenheader.
8.3 Session Model
Access token validation is not purely stateless. JwtAuthGuard validates the session with SessionService, so server-side session revocation and idle/absolute timeout policies can affect subsequent API calls.
9. Authorization And Scope Model
Current authorization layers:
| Layer | Implementation | Status |
|---|---|---|
| JWT authentication | JwtAuthGuard |
CONFIRMED |
| Role metadata | @Roles(...) + RolesGuard |
PARTIAL usage |
| Admin-only checks | AdminGuard in users module |
TEMPORARY BYPASS present |
| Farm/pond scope | farmIds, pondIds, services/use cases |
PARTIAL, many temporary bypasses |
| IoT ingest token | IotIngestTokenGuard |
CONFIRMED |
Known risk:
- Multiple services/use cases contain
TEMPORARY BYPASScomments for role, farm, pond and historical-record checks. AdminGuardcurrently returnstrueeven when the user is not ADMIN because theForbiddenExceptionis commented out.- This must be treated as production-readiness risk, not as final permission behavior.
Architecture rule:
- Any new protected business endpoint should explicitly document its auth guard, role requirement and farm/pond scope behavior.
- Temporary bypasses must be tracked before production hardening.
10. IoT API Architecture
IoT-facing APIs are under:
/api/iot/v1
Current route groups:
- telemetry ingest;
- command acknowledgement;
- device registration list/detail/map/assign;
- device registration enable/disable;
- device registration controls;
- output mode update.
Security:
- Telemetry and command ACK use
IotIngestTokenGuard. - Guard expects
IOT_INGEST_TOKEN. - Token is compared using
timingSafeEqual.
Integration role:
MQTT device / iot-worker
-> /api/iot/v1/telemetry
-> backend validation and persistence
-> sensors/readings/devices/alerts domain data
Production gap:
- IoT production topology is still documented as TBD in deployment docs.
- Device provisioning and production token rotation policy should be finalized.
11. Request And Response Conventions
11.1 Validation
Global validation behavior:
- strips unknown fields through
whitelist; - rejects non-whitelisted fields through
forbidNonWhitelisted; - transforms payloads based on DTO metadata;
- keeps implicit conversion disabled.
DTO rule:
- Request DTOs should define validation decorators for all accepted fields.
- Query DTOs should define type transformation explicitly where numeric/boolean values are expected.
11.2 Standard Response Shapes
Item response:
{
"data": {},
"message": "Thanh cong"
}
Paginated response:
{
"data": [],
"meta": {
"page": 1,
"take": 10,
"itemCount": 0,
"pageCount": 0,
"hasPreviousPage": false,
"hasNextPage": false
},
"message": "Thanh cong"
}
Common pagination fields:
| Field | Default | Rule |
|---|---|---|
search |
"" |
string |
order |
DESC |
enum |
orderBy |
createdAt |
string |
page |
1 |
integer, min 1 |
take |
10 |
integer, min 1, max 100 |
11.3 Error Handling
Global Prisma exception mapping:
| Prisma code | HTTP status | Meaning |
|---|---|---|
P2000 |
400 |
Value too long |
P2002 |
409 |
Unique constraint |
P2003 |
400 |
Foreign key violation |
P2025 |
404 |
Record not found |
| other known Prisma error | 500 |
Generic database error |
Validation and auth errors follow NestJS defaults unless overridden by controller/use-case code.
12. Data Access Architecture
Primary database access:
PrismaService -> Prisma Client -> PostgreSQL/TimescaleDB
Domain data clusters:
| Cluster | Models |
|---|---|
| Auth/user | User, Session, AuditLog, FarmMember, PondAssignment |
| Farm/pond/crop | Farm, Pond, Crop, CropSpecies, CropSizeRange |
| Logs/feeding | ManualWaterRecord, MineralRecord, SiphonRecord, ProductivityRecord, FarmingLogAttachment, FeedingRecord, FeedType |
| IoT/device | Sensor, SensorReading, ParameterThreshold, Device, DeviceCommandLog, AutoRule |
| Alerts/tickets | Alert, AlertStatusHistory, Ticket, TicketAttachment, TicketComment, TicketStatusHistory, TicketWaterParameter |
| Knowledge/notification | Notification, NotificationConfig, HandbookArticle, HandbookVersion, ArticleBookmark |
| Reports/location | ReportHistory, Province, Ward |
Database migration rule:
- Production should use
prisma migrate deploy. prisma db pushis not a production migration strategy.- Seed should not run in production unless explicitly approved and idempotent.
13. Client Integration Architecture
13.1 Web Client
Web API integration:
- Axios config under
web/src/core/config/axios.config.ts. - Endpoint constants under
web/src/core/constants/api-url.ts. - Feature service wrappers under
web/src/core/services/*.
Architecture rule:
- Web pages/components should call service wrappers and hooks, not hardcode raw endpoints in UI components.
- Endpoint constants intentionally omit
/api; the base URL should include the API prefix when configured.
13.2 Mobile Client
Mobile API integration:
- API client under
mobile/src/core/config/api-client.ts. - Endpoint constants under
mobile/src/core/constants/apiURL.constants.ts. - Feature service wrappers under
mobile/src/core/services/api/*.
Mobile-specific auth behavior:
- Mobile stores tokens client-side.
- Mobile refresh flow can send
x-client-type: mobileandx-refresh-token.
14. External Integrations
| Integration | Current API role |
|---|---|
| PostgreSQL/TimescaleDB | Primary transactional and time-series persistence |
| Redis | Session/cache provider when REDIS_HOST is configured |
| Google Cloud Storage | Attachments, report files and uploaded objects |
| Gmail SMTP or console email | Forgot password and email notifications |
| MQTT/IoT worker | Telemetry, device command lifecycle and command ACK |
| Swagger/OpenAPI | Runtime API documentation |
15. Observability And Operations
Current confirmed behavior:
- Successful API requests are logged by method, URL, status and duration.
- Swagger asset requests are excluded from the success log.
- Health endpoint exists at
/api/health. - Status endpoint exists at
/api/status.json. - Prisma known request errors are normalized by a global filter.
Current gaps:
- No production metrics provider is confirmed.
- No distributed tracing is confirmed.
- No centralized log provider is confirmed.
- No API SLO/SLI definitions are confirmed.
Recommended API signals:
| Signal | Purpose |
|---|---|
| Request rate by route/status | Detect traffic and error spikes |
| P95/P99 latency | Detect slow API paths |
| Auth failures | Detect credential or session issues |
| Prisma error codes | Detect data consistency and unique constraint issues |
| IoT ingest failures | Detect device/worker integration problems |
| Report/export failures | Detect async job issues |
| GCS/email failures | Detect external provider issues |
16. Versioning And Compatibility
Current versioning:
- Main app APIs are not URL-versioned beyond the global
/apiprefix. - IoT API uses route-level version namespace
/iot/v1. - Swagger document version is
1.0.0.
Compatibility rules:
- Breaking response-shape changes must update
08_API Specification.md, client services, QA test cases and release notes. - New public routes should include Swagger decorators and DTO validation.
- If route-level versioning expands beyond IoT, define a consistent API versioning policy before adding
/v2routes.
17. Security And Production Readiness Risks
| ID | Risk | Impact | Required follow-up |
|---|---|---|---|
| API-ARCH-01 | Temporary auth/scope bypasses exist in multiple modules | Users may access or mutate data beyond final intended scope | Remove bypasses or document approved exception before production |
| API-ARCH-02 | AdminGuard currently does not block non-admin users | Admin-only APIs may be exposed | Re-enable ForbiddenException and regression-test admin routes |
| API-ARCH-03 | Some modules have uneven layering | Changes may bypass consistent authorization/validation patterns | Standardize new work around controller -> use case/service -> repository |
| API-ARCH-04 | No confirmed metrics/tracing provider | Incidents may be harder to diagnose | Add centralized logs, metrics and alerting |
| API-ARCH-05 | IoT token is a shared bearer token | Token leakage could expose ingest endpoints | Define rotation, scope and device provisioning strategy |
| API-ARCH-06 | Main REST API has no formal URL versioning policy | Future breaking changes may be hard to coordinate | Define versioning policy before major API changes |
| API-ARCH-07 | Web/mobile endpoint constants can drift from controllers | Client regressions | Keep 08_API Specification.md and service constants updated per API change |
18. Change Rules
When changing API architecture or contracts:
- Update controller DTOs and Swagger decorators.
- Update
docs/08_API Specification.mdfor endpoint contract changes. - Update this file for runtime/layer/security/integration architecture changes.
- Update web/mobile endpoint constants and service wrappers.
- Add or update tests for auth, scope and response behavior.
- Record release notes for breaking changes.
19. Traceability
| Source | Architecture evidence |
|---|---|
backend/main.ts |
Runtime bootstrap, prefix, CORS, validation, Swagger, logging |
backend/app.module.ts |
Global modules, throttler, Prisma filter and feature module imports |
backend/common/common.module.ts |
Prisma, cache, email and GCS providers |
backend/common/dtos/* |
Response and pagination conventions |
backend/common/filters/prisma-exception.filter.ts |
Prisma error mapping |
backend/modules/auth/** |
Access token, refresh token and session architecture |
backend/modules/iot-ingest/** |
IoT ingest API and token guard |
backend/modules/**/**.controller.ts |
Route map and API surface |
web/src/core/config/axios.config.ts |
Web API client runtime |
web/src/core/constants/api-url.ts |
Web endpoint constants |
mobile/src/core/config/api-client.ts |
Mobile API client runtime |
mobile/src/core/constants/apiURL.constants.ts |
Mobile endpoint constants |
docs/08_API Specification.md |
Endpoint-level API specification |