Backend for the Smart Agriculture Platform — Group C3 (System Engineering & Interaction)
This is the BFF (Backend For Frontend) that connects the farmer-facing React dashboard to C1's IoT sensors (via Kafka), C2's AI/ML models (via REST), and C4's infrastructure (via Keycloak + Prometheus).
C1 Sensors (ESP32) C2 AI Models (FastAPI)
│ │
[Kafka] [HTTP/REST]
│ │
▼ ▼
┌──────────────────────────────────────────┐
│ C3 FastAPI Backend │
│ │
│ • Consumes sensor data from Kafka │
│ • Proxies AI predictions from C2 │
│ • Pushes live data via Socket.IO │
│ • Exposes REST API for the dashboard │
│ • Serves Prometheus metrics for C4 │
└──────────────────────────────────────────┘
│ │
[Socket.IO] [REST API]
│ │
▼ ▼
React Dashboard
(agri-dashboard frontend)
It does not store its own data — C2 owns the database (PostgreSQL + InfluxDB). This backend is a pass-through layer that translates, aggregates, and streams data between the frontend and the other subgroups.
| Technology | Purpose |
|---|---|
| FastAPI | Web framework — handles REST endpoints |
| aiokafka | Kafka consumer — receives C1 sensor data asynchronously |
| python-socketio | WebSocket server — pushes live data to the React frontend |
| httpx | Async HTTP client — calls C2's FastAPI endpoints |
| pydantic-settings | Config management — reads .env file |
| prometheus-fastapi-instrumentator | Metrics — auto-exposes /metrics for C4's Prometheus |
| uvicorn | ASGI server — runs the FastAPI app |
c3-backend/
│
├── .env ← Your secrets (git-ignored)
├── .env.example ← Template for teammates
├── .gitignore
├── Dockerfile ← For C4 to containerise
├── requirements.txt ← Python dependencies
│
├── venv/ ← Virtual environment (do not edit)
│
└── app/
├── __init__.py
├── main.py ← Entry point — starts everything
│
├── config/
│ ├── __init__.py
│ └── settings.py ← Reads .env, exports settings object
│
├── middleware/
│ ├── __init__.py
│ └── auth.py ← Mock auth now, Keycloak later
│
├── models/
│ ├── __init__.py
│ ├── sensor.py ← KafkaMessage + ZoneState schemas
│ └── irrigation.py ← IrrigationZone + TriggerRequest schemas
│
├── routes/
│ ├── __init__.py
│ ├── sensors.py ← GET /api/sensors
│ ├── irrigation.py ← GET/POST /api/irrigation/*
│ ├── analytics.py ← GET /api/analytics/yield, /soil-forecast
│ ├── weather.py ← GET /api/weather/forecast (Open-Meteo)
│ ├── alerts.py ← GET /api/alerts/anomalies
│ ├── growth.py ← GET /api/growth/stage
│ ├── water_stress.py ← GET /api/water-stress
│ └── satellite.py ← GET /api/satellite/ndvi
│
└── services/
├── __init__.py
├── kafka_consumer.py ← Consumes C1 sensor data, aggregates by zone
├── socket_service.py ← Pushes live data to React via Socket.IO
└── c2_proxy.py ← HTTP calls to C2's FastAPI
- Python 3.11+
- pip
- A code editor (VS Code recommended)
# 1. Clone the repo
git clone https://github.com/AgriSenseNet/agri-backend.git
cd agri-backend
# 2. Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate # macOS/Linux
# venv\Scripts\activate # Windows
# 3. Install dependencies
pip install -r requirements.txt
# 4. Create your .env file
cp .env.example .env
# Edit .env with your actual values
# 5. Start the server
uvicorn app.main:app --reload --port 3001| URL | Expected |
|---|---|
| http://localhost:3001/health | {"status": "ok"} |
| http://localhost:3001/docs | Swagger UI (interactive API docs) |
| http://localhost:3001/metrics | Prometheus metrics text |
| http://localhost:3001/api/sensors | {"count": 0, "zones": []} |
| http://localhost:3001/api/irrigation/zones | 4 mock irrigation zones |
| Method | Path | Description |
|---|---|---|
| GET | /api/sensors |
All zones with latest aggregated readings |
| GET | /api/sensors/{zone_id} |
One specific zone |
| Method | Path | Description |
|---|---|---|
| GET | /api/irrigation/zones |
All irrigation zones |
| POST | /api/irrigation/trigger |
Toggle irrigation on/off |
| POST | /api/irrigation/recommendation |
AI recommendation from C2 |
| Method | Path | Description |
|---|---|---|
| GET | /api/analytics/yield |
Crop yield prediction |
| GET | /api/analytics/soil-forecast |
Soil moisture forecast (24–72h) |
| Method | Path | Description |
|---|---|---|
| GET | /api/weather/forecast |
5-day forecast from Open-Meteo |
| Method | Path | Description |
|---|---|---|
| GET | /api/alerts/anomalies |
Sensor anomaly detections from C2 |
| Method | Path | Description |
|---|---|---|
| GET | /api/growth/stage |
Crop growth stage classification |
| GET | /api/water-stress |
Evapotranspiration & water stress index |
| GET | /api/satellite/ndvi |
NDVI satellite vegetation data |
| Method | Path | Description |
|---|---|---|
| GET | /health |
Kubernetes health check |
| GET | /metrics |
Prometheus metrics |
C1 ESP32 sensors and bridge
→ MQTT bridge topics
→ Kafka topics: "iot-sensors", "iot-device-status", "iot-logs"
→ C3 aiokafka consumer (kafka_consumer.py)
→ Aggregates sensor telemetry into zone_states dict
→ Stores latest device availability per zone and recent log events
→ python-socketio broadcasts sensor/device/log updates
→ React frontend receives live data via socket.io-client
Sensor telemetry messages contain one sensor reading for one zone:
{"zone": "zone1", "sensor": "soil_moisture", "value": 42, "unit": "%", "ts": 1746172800000}The consumer aggregates these into a complete zone object:
{"zone": "zone1", "soil_moisture": 42, "temperature": 24.5, "humidity": 55, ...}Device availability messages arrive on iot-device-status:
{"zone": "zone2", "status": "online", "ts": 1746172800000}Log events arrive on iot-logs:
{"zone": "zone1", "ts": 1746172800, "type": "system", "msg": "pump restarted"}React frontend
→ C3 FastAPI route (e.g. /api/analytics/yield)
→ c2_proxy.py calls C2's FastAPI (e.g. /predict/yield)
→ Response forwarded back to frontend
| Variable | Default | Description |
|---|---|---|
PORT |
3001 |
Server port |
FRONTEND_URL |
http://localhost:5173 |
React app URL (for CORS) |
KAFKA_BROKER |
kafka.cropwise.garden:9095 |
Kafka broker bootstrap servers |
KAFKA_TOPIC |
iot-sensors |
Backward-compatible alias for the sensor topic |
KAFKA_SENSOR_TOPIC |
iot-sensors |
Sensor telemetry Kafka topic |
KAFKA_DEVICE_STATUS_TOPIC |
iot-device-status |
Device availability Kafka topic |
KAFKA_LOGS_TOPIC |
iot-logs |
Log/event Kafka topic |
KAFKA_GROUP_ID |
c3-dashboard-group |
Consumer group name |
C2_API_URL |
http://localhost:8000 |
C2's FastAPI base URL |
AUTH_MODE |
mock |
mock or keycloak |
- Connection: Kafka consumer on topics
iot-sensors,iot-device-status, andiot-logs - Broker:
kafka.cropwise.garden:9095 - Message format: Sensor telemetry, device availability, and bridge log events
- Our consumer group:
c3-dashboard-group
- Connection: HTTP calls via
httpx.AsyncClient - 6 ML model endpoints + 1 satellite CR endpoint
- Note: C2 endpoints have no
/apiprefix (e.g./predict/yieldnot/api/predict/yield) - Weather: C2 does not expose weather — C3 calls Open-Meteo directly
- Deployment: Docker container on Kubernetes (port 3001)
- Auth: Keycloak OIDC (mock mode for development)
- Monitoring: Prometheus scrapes
/metrics - Health: Kubernetes probes hit
/health - WebSocket: Socket.IO on
/socket.iopath — Kong must allow WebSocket upgrade
Open two terminals:
Terminal 1 — Backend
cd c3-backend
source venv/bin/activate
uvicorn app.main:app --reload --port 3001Terminal 2 — Frontend
cd agri-dashboard
npm run devOpen http://localhost:5173 in your browser.
# Build
docker build -t c3-backend .
# Run
docker run -p 3001:3001 --env-file .env c3-backend| Error | Fix |
|---|---|
ModuleNotFoundError: No module named 'app' |
Run uvicorn from the c3-backend/ folder, not inside app/ |
ModuleNotFoundError: No module named 'fastapi' |
Activate your venv: source venv/bin/activate |
Address already in use |
Another process on port 3001. Kill it or change PORT in .env |
[Kafka] Failed to connect |
Kafka broker not reachable — REST endpoints still work |
CORS error in browser |
FRONTEND_URL in .env must match your Vite dev URL exactly |
422 Unprocessable Entity |
Request body doesn't match the Pydantic model — check /docs |
502 C2 error |
C2's FastAPI is not running or unreachable |
git pull origin main
# Create a branch for your feature
git checkout -b feat/kafka-ssl-config
# Do your work, then commit
git add .
git commit -m "feat: add SSL config for Kafka port 9094"
# Push your branch
git push origin feat/kafka-ssl-config
# Then open a Pull Request on GitHub to merge into main