# CropWatch API (NestJS)
CropWatch’s REST API, built with NestJS and backed by Supabase.
- Landing page (static):
GET / - Swagger UI:
GET /docs
Note on versioning: Swagger is configured with a base path of
v23(seesrc/main.ts). In local dev, routes are served at the root (for exampleGET /air/:dev_eui). In production you may be behind a path prefix/rewrite.
- Node.js 20 (CI uses Node 20)
- pnpm (recommended via Corepack)
Enable pnpm via Corepack:
corepack enable
corepack prepare --activateInstall dependencies:
pnpm installRun the API:
pnpm run start:devThen open:
- http://localhost:3000/ (landing page)
- http://localhost:3000/docs (Swagger UI)
Most API endpoints are protected with JWT bearer auth via JwtAuthGuard.
- Get a bearer token:
curl -sS -X POST \
http://localhost:3000/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"user@example.com","password":"StrongPassword123!"}'- Use the returned access token:
curl -sS \
http://localhost:3000/devices \
-H 'Authorization: Bearer YOUR_JWT'Swagger documents an x-api-key header. At the code level, this repository currently advertises the header in Swagger (addApiKey(...)) but does not enforce it with a guard/middleware in NestJS. If your deployment requires an API key, enforcement likely happens upstream (edge/proxy) or is planned.
Routes are defined in the feature controllers under src/*/*.controller.ts.
authPOST /auth/login– Supabase password login, returns bearer token payloadGET /auth– returns authenticated user payload (requires bearer)
devicesGET /devices– list user devices (requires bearer)GET /devices/:dev_eui– fetch one device (requires bearer)
- Telemetry (requires bearer)
GET /air/:dev_euiGET /soil/:dev_euiGET /water/:dev_euiGET /traffic/:dev_eui- All support query params
start,end(ISO 8601) andtimezone(IANA, defaults toUTC)
powerGET /power/:id– placeholder/example (not JWT-protected in the controller)
- Realtime (WebSocket)
src/realtime/realtime.gateway.tsdefines message handlers (Socket.IO)
For the authoritative contract (including DTO shapes), use Swagger at /docs.
Each “resource” follows the same Nest module layout:
src/<resource>/
<resource>.module.ts
<resource>.controller.ts
<resource>.service.ts
<resource>.controller.spec.ts
<resource>.service.spec.ts
dto/
entities/
Cross-cutting/shared functionality lives under:
src/common/(shared DTOs + shared services likeTimezoneFormatterService)src/supabase/(Supabase client providers +SupabaseService)src/auth/(JWT strategy/guard + login flow)
Use this as the “pit of success” checklist for adding a new feature module.
- Create the module skeleton
- Create
src/<resource>/and add:<resource>.module.ts<resource>.controller.ts<resource>.service.tsdto/andentities/folders as needed
- Wire it into the app
- Import your module in
src/app.module.ts(imports: [...]).
- Implement the service (data access)
- If you read/write Supabase, inject
SupabaseServiceand usesupabaseService.getClient(). - If you return time-series data with timestamps/timezones, prefer
TimezoneFormatterService(fromsrc/common/) instead of duplicating time formatting logic.
- Implement the controller (HTTP)
- Use
@Controller('<resource>')for your route prefix. - Add
@UseGuards(JwtAuthGuard)for protected endpoints. - Validate inputs at the boundary (controller):
dev_euirequiredstart/endparseable dates andstart <= endtimezoneoptional, defaults toUTC
- Keep Swagger accurate
- Add
@ApiOkResponse,@ApiBadRequestResponse, etc. - Use DTOs from
dto/astype:in Swagger decorators.
- Add tests
- Add/extend unit tests:
<resource>.service.spec.ts(mock Supabase calls)<resource>.controller.spec.ts
Tip: controller specs need providers for constructor-injected dependencies. If your service constructor injects SupabaseService and TimezoneFormatterService, your controller spec module should provide them (mock SupabaseService, real or mock TimezoneFormatterService).
- Run the local quality gate
pnpm run lint
pnpm run test
pnpm run test:cov
pnpm run build- Branch from
master - Suggested branch names:
feature/<short-name>,fix/<short-name>,chore/<short-name>
- Ensure
pnpm run test:covpasses locally - Ensure
pnpm run buildpasses - If you changed an endpoint/DTO, verify
/docslooks correct
GitHub Actions runs on pushes to master (and on releases) and performs:
pnpm install --frozen-lockfilepnpm run test:covpnpm run build- Coverage upload to QLTY (requires repo secret
QLTY_COVERAGE_TOKEN)
Workflow file: .github/workflows/npm-publish.yml (named “API CI Gate”).
- Clear problem statement + approach in the PR description
- Swagger decorators updated for any API changes
- Tests added/updated for behavior changes
- No new duplicated logic (prefer shared helpers/services under
src/common/)