AgriChain Backend API

System Overview

The AgriChain backend provides RESTful APIs for the smart farming dashboard, handling IoT device data, blockchain transactions, AI predictions, and user management.

Tech Stack

  • Node.js v18+
  • Express.js
  • MongoDB (with Mongoose)
  • JWT Authentication
  • Web3.js for blockchain
  • TensorFlow.js for AI

Key Features

  • Real-time IoT data processing
  • Blockchain data integrity
  • AI prediction models
  • User authentication
  • Cloud integration

Deployment

  • Docker containerization
  • Kubernetes for orchestration
  • AWS ECS/EKS
  • CI/CD with GitHub Actions

Installation


# Clone the repository
git clone https://github.com/agrichain/backend.git
cd backend

# Install dependencies
npm install

# Set up environment variables
cp .env.example .env
# Edit .env with your configuration

# Start the development server
npm run dev

# For production
npm run build
npm start
            

API Endpoints

Authentication

POST /api/auth/register

// Request Body
{
    "name": "John Farmer",
    "email": "john@example.com",
    "password": "securepassword123",
    "farmName": "Green Valley Farms"
}

// Response
{
    "success": true,
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "user": {
        "id": "507f1f77bcf86cd799439011",
        "name": "John Farmer",
        "email": "john@example.com",
        "farmName": "Green Valley Farms"
    }
}
                
POST /api/auth/login

// Request Body
{
    "email": "john@example.com",
    "password": "securepassword123"
}

// Response (same structure as register)
                

IoT Device Management

GET /api/iot/devices

// Response
{
    "success": true,
    "devices": [
        {
            "id": "60d5ec9cfb9d8b3a48fe9a12",
            "name": "Soil Moisture Sensor - Field A",
            "type": "moisture",
            "status": "online",
            "batteryLevel": 78,
            "signalStrength": 92,
            "lastReading": {
                "value": 72,
                "timestamp": "2023-06-15T08:30:45.000Z"
            },
            "location": {
                "field": "A",
                "coordinates": [34.0522, -118.2437]
            }
        },
        // ... more devices
    ]
}
                
POST /api/iot/data (for IoT devices to send data)

// Request Body
{
    "deviceId": "60d5ec9cfb9d8b3a48fe9a12",
    "secretKey": "device-secret-key",
    "readings": [
        {
            "type": "moisture",
            "value": 72,
            "timestamp": "2023-06-15T08:30:45.000Z"
        }
    ]
}

// Response
{
    "success": true,
    "message": "Data received and stored",
    "blockchainHash": "a3f8e2d4b5c71f9e8a0b6d5c4f3e2a1b"
}
                

Blockchain

GET /api/blockchain/blocks

// Response
{
    "success": true,
    "blocks": [
        {
            "index": 4892,
            "timestamp": "2023-06-15T08:30:45.000Z",
            "data": {
                "deviceId": "60d5ec9cfb9d8b3a48fe9a12",
                "readingType": "moisture",
                "value": 72,
                "field": "A"
            },
            "previousHash": "b5c71f9e8a0b6d5c4f3e2a1ba3f8e2d",
            "hash": "a3f8e2d4b5c71f9e8a0b6d5c4f3e2a1b"
        },
        // ... more blocks
    ]
}
                
GET /api/blockchain/validate

// Response
{
    "success": true,
    "valid": true,
    "invalidBlocks": [],
    "message": "Blockchain integrity verified"
}
                

AI Predictions

GET /api/ai/predictions

// Response
{
    "success": true,
    "predictions": [
        {
            "type": "irrigation",
            "field": "A",
            "prediction": "Next irrigation in 8 hours",
            "confidence": 0.92,
            "recommendation": "Delay watering until evening to reduce evaporation"
        },
        {
            "type": "pest",
            "field": "B",
            "prediction": "Low pest risk (15%)",
            "confidence": 0.87,
            "recommendation": "Monitor for corn borer, no treatment needed yet"
        },
        {
            "type": "harvest",
            "field": "C",
            "prediction": "Optimal harvest window: July 15-18",
            "confidence": 0.85,
            "recommendation": "Prepare workforce and storage for peak yield"
        }
    ]
}
                
POST /api/ai/train (Admin only - retrain models)

// Response
{
    "success": true,
    "message": "AI models retrained successfully",
    "newAccuracy": {
        "irrigation": 0.93,
        "pest": 0.88,
        "harvest": 0.86
    }
}
                

Cloud Integration

GET /api/cloud/status

// Response
{
    "success": true,
    "services": [
        {
            "provider": "AWS IoT",
            "status": "connected",
            "metrics": {
                "messages": 1248,
                "storage": 2.8,
                "rulesProcessed": 56
            }
        },
        {
            "provider": "Azure Data Lake",
            "status": "syncing",
            "metrics": {
                "dataStored": 4.2,
                "lastSync": "2023-06-15T08:28:12.000Z",
                "analyticsJobs": 12
            }
        },
        {
            "provider": "Google BigQuery",
            "status": "processing",
            "metrics": {
                "queries": 84,
                "mlModels": 3,
                "storage": 1.5
            }
        }
    ]
}
                

Database Schema

User


{
    name: String,
    email: { type: String, unique: true },
    password: String,
    farmName: String,
    role: { type: String, enum: ['user', 'admin'], default: 'user' },
    createdAt: { type: Date, default: Date.now }
}
                    

Device


{
    name: String,
    type: { type: String, enum: ['moisture', 'temperature', 'humidity', 'light', 'ph', 'co2'] },
    status: { type: String, enum: ['online', 'offline', 'maintenance'] },
    batteryLevel: Number,
    signalStrength: Number,
    secretKey: String, // For authentication
    userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    location: {
        field: String,
        coordinates: [Number] // [longitude, latitude]
    }
}
                    

Reading


{
    deviceId: { type: mongoose.Schema.Types.ObjectId, ref: 'Device' },
    type: String,
    value: Number,
    timestamp: { type: Date, default: Date.now },
    field: String,
    blockchainHash: String
}
                    

Block


{
    index: Number,
    timestamp: { type: Date, default: Date.now },
    data: {
        deviceId: { type: mongoose.Schema.Types.ObjectId, ref: 'Device' },
        readingType: String,
        value: Number,
        field: String
    },
    previousHash: String,
    hash: String
}
                    

Prediction


{
    type: { type: String, enum: ['irrigation', 'pest', 'harvest'] },
    field: String,
    prediction: String,
    confidence: Number,
    recommendation: String,
    userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    createdAt: { type: Date, default: Date.now }
}
                    

CloudService


{
    provider: { type: String, enum: ['aws', 'azure', 'google'] },
    status: String,
    metrics: {
        messages: Number,
        storage: Number,
        rulesProcessed: Number,
        // ... other metrics
    },
    lastSync: Date,
    userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
}
                    

Example Server Code

app.js (Main Server File)


require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const { Web3 } = require('web3');
const tf = require('@tensorflow/tfjs-node');

const app = express();

// Database connection
mongoose.connect(process.env.MONGODB_URI, {
    useNewUrlParser: true,
    useUnifiedTopology: true
})
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('MongoDB connection error:', err));

// Middleware
app.use(cors());
app.use(helmet());
app.use(express.json());

// Rate limiting
const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100 // limit each IP to 100 requests per windowMs
});
app.use(limiter);

// Blockchain setup
const web3 = new Web3(process.env.BLOCKCHAIN_NODE_URL);
const contract = new web3.eth.Contract(
    JSON.parse(process.env.CONTRACT_ABI),
    process.env.CONTRACT_ADDRESS
);

// Routes
app.use('/api/auth', require('./routes/auth'));
app.use('/api/iot', require('./routes/iot'));
app.use('/api/blockchain', require('./routes/blockchain'));
app.use('/api/ai', require('./routes/ai'));
app.use('/api/cloud', require('./routes/cloud'));

// Error handling
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).json({ 
        success: false, 
        message: 'Internal server error' 
    });
});

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

module.exports = app;
                

routes/iot.js (Example Route)


const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const Device = require('../models/Device');
const Reading = require('../models/Reading');
const { addBlock } = require('../utils/blockchain');

// @route   GET api/iot/devices
// @desc    Get all user's IoT devices
// @access  Private
router.get('/devices', auth, async (req, res) => {
    try {
        const devices = await Device.find({ userId: req.user.id });
        
        // Get latest readings for each device
        const devicesWithReadings = await Promise.all(devices.map(async device => {
            const latestReading = await Reading.findOne({ deviceId: device._id })
                .sort({ timestamp: -1 })
                .limit(1);
                
            return {
                ...device.toObject(),
                lastReading: latestReading || null
            };
        }));
        
        res.json({ success: true, devices: devicesWithReadings });
    } catch (err) {
        console.error(err.message);
        res.status(500).json({ success: false, message: 'Server error' });
    }
});

// @route   POST api/iot/data
// @desc    Receive data from IoT device
// @access  Private (using device secret key)
router.post('/data', async (req, res) => {
    const { deviceId, secretKey, readings } = req.body;
    
    try {
        // Verify device
        const device = await Device.findOne({ _id: deviceId, secretKey });
        if (!device) {
            return res.status(401).json({ 
                success: false, 
                message: 'Invalid device credentials' 
            });
        }
        
        // Save readings
        const savedReadings = await Promise.all(readings.map(async reading => {
            const newReading = new Reading({
                deviceId,
                type: reading.type,
                value: reading.value,
                timestamp: reading.timestamp || Date.now(),
                field: device.location.field
            });
            
            // Add to blockchain
            const blockData = {
                deviceId,
                readingType: reading.type,
                value: reading.value,
                field: device.location.field
            };
            
            const hash = await addBlock(blockData);
            newReading.blockchainHash = hash;
            
            return await newReading.save();
        }));
        
        // Update device status and battery if provided
        if (readings[0].batteryLevel) {
            device.batteryLevel = readings[0].batteryLevel;
            await device.save();
        }
        
        res.json({ 
            success: true, 
            message: 'Data received and stored',
            blockchainHash: savedReadings[0].blockchainHash 
        });
    } catch (err) {
        console.error(err.message);
        res.status(500).json({ success: false, message: 'Server error' });
    }
});

module.exports = router;
                

utils/blockchain.js


const Block = require('../models/Block');
const crypto = require('crypto');

// Simple blockchain implementation (in production would use Ethereum or similar)
class Blockchain {
    constructor() {
        this.chain = [];
        this.initializeChain();
    }
    
    async initializeChain() {
        // Load existing chain from database
        const blocks = await Block.find().sort({ index: 1 });
        this.chain = blocks;
        
        // If no blocks, create genesis block
        if (this.chain.length === 0) {
            await this.createGenesisBlock();
        }
    }
    
    async createGenesisBlock() {
        const genesisBlock = new Block({
            index: 0,
            timestamp: Date.now(),
            data: { message: "Genesis block" },
            previousHash: "0",
            hash: this.calculateHash(0, "0", Date.now(), { message: "Genesis block" })
        });
        
        await genesisBlock.save();
        this.chain.push(genesisBlock);
    }
    
    calculateHash(index, previousHash, timestamp, data) {
        return crypto
            .createHash('sha256')
            .update(index + previousHash + timestamp + JSON.stringify(data))
            .digest('hex');
    }
    
    async addBlock(data) {
        const previousBlock = this.chain[this.chain.length - 1];
        const newIndex = previousBlock.index + 1;
        const newTimestamp = Date.now();
        const newHash = this.calculateHash(newIndex, previousBlock.hash, newTimestamp, data);
        
        const newBlock = new Block({
            index: newIndex,
            timestamp: newTimestamp,
            data,
            previousHash: previousBlock.hash,
            hash: newHash
        });
        
        await newBlock.save();
        this.chain.push(newBlock);
        
        return newHash;
    }
    
    async validateChain() {
        for (let i = 1; i < this.chain.length; i++) {
            const currentBlock = this.chain[i];
            const previousBlock = this.chain[i - 1];
            
            // Check hash
            if (currentBlock.hash !== this.calculateHash(
                currentBlock.index,
                currentBlock.previousHash,
                currentBlock.timestamp,
                currentBlock.data
            )) {
                return false;
            }
            
            // Check previous hash
            if (currentBlock.previousHash !== previousBlock.hash) {
                return false;
            }
        }
        
        return true;
    }
}

const blockchain = new Blockchain();

// Public functions
const addBlock = async (data) => {
    return await blockchain.addBlock(data);
};

const validateChain = async () => {
    return await blockchain.validateChain();
};

const getBlocks = async () => {
    return blockchain.chain;
};

module.exports = {
    addBlock,
    validateChain,
    getBlocks
};
                

Deployment

Dockerfile


# Use official Node.js image
FROM node:18-alpine

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
COPY package*.json ./
RUN npm install

# Bundle app source
COPY . .

# Build the app
RUN npm run build

# Expose the app port
EXPOSE 5000

# Start the app
CMD ["npm", "start"]
                

docker-compose.yml


version: '3.8'

services:
  app:
    build: .
    ports:
      - "5000:5000"
    environment:
      - NODE_ENV=production
      - MONGODB_URI=mongodb://mongo:27017/agrichain
      - JWT_SECRET=your_jwt_secret
    depends_on:
      - mongo
    restart: unless-stopped

  mongo:
    image: mongo:6.0
    ports:
      - "27017:27017"
    volumes:
      - mongodb_data:/data/db
    environment:
      - MONGO_INITDB_ROOT_USERNAME=root
      - MONGO_INITDB_ROOT_PASSWORD=example

volumes:
  mongodb_data:
                

Kubernetes Deployment


apiVersion: apps/v1
kind: Deployment
metadata:
  name: agrichain-backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: agrichain-backend
  template:
    metadata:
      labels:
        app: agrichain-backend
    spec:
      containers:
      - name: app
        image: your-registry/agrichain-backend:latest
        ports:
        - containerPort: 5000
        env:
        - name: NODE_ENV
          value: production
        - name: MONGODB_URI
          value: "mongodb://agrichain-mongo:27017/agrichain"
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: agrichain-secrets
              key: jwt-secret
      - name: mongo
        image: mongo:6.0
        ports:
        - containerPort: 27017
        env:
        - name: MONGO_INITDB_ROOT_USERNAME
          valueFrom:
            secretKeyRef:
              name: agrichain-secrets
              key: mongo-root-username
        - name: MONGO_INITDB_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: agrichain-secrets
              key: mongo-root-password
        volumeMounts:
        - name: mongodb-data
          mountPath: /data/db
      volumes:
      - name: mongodb-data
        persistentVolumeClaim:
          claimName: mongodb-pvc

---
apiVersion: v1
kind: Service
metadata:
  name: agrichain-service
spec:
  selector:
    app: agrichain-backend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 5000
  type: LoadBalancer
                

Next Steps

Made with DeepSite LogoDeepSite - 🧬 Remix