# Backend API - Dashboard Solusi AI Peternakan Ayam Backend server dengan Express.js dan PostgreSQL database untuk menyimpan data siklus produksi dan mortalitas ayam. ## ๐Ÿ“‹ Daftar Isi - [Teknologi](#teknologi) - [Instalasi](#instalasi) - [Cara Menjalankan](#cara-menjalankan) - [Struktur File](#struktur-file) - [Database](#database) - [API Endpoints](#api-endpoints) - [Environment Variables](#environment-variables) - [Development](#development) ## ๐Ÿ›  Teknologi - **Node.js** v18+ - **Express.js** - Web framework - **pg** - PostgreSQL database driver - **cors** - Cross-origin resource sharing - **dotenv** - Environment variables ## ๐Ÿ’ฟ Instalasi ```bash cd backend npm install ``` ## โ–ถ๏ธ Cara Menjalankan ### Development Mode ```bash npm run dev ``` Server akan berjalan di http://localhost:5001 ### Production Mode ```bash npm start ``` ### Seeding Database ```bash npm run seed ``` Akan mengisi database dengan 4 siklus produksi awal. ## ๐Ÿ“ Struktur File ``` backend/ โ”œโ”€โ”€ database/ โ”‚ โ”œโ”€โ”€ db.js # PostgreSQL connection pool & initialization โ”‚ โ”œโ”€โ”€ schema.sql # PostgreSQL schema definition โ”‚ โ”œโ”€โ”€ seed-postgres.js # Data seeding script โ”‚ โ”œโ”€โ”€ run-migrations.js # Migration runner โ”‚ โ””โ”€โ”€ migrations/ # Database migration files โ”œโ”€โ”€ models/ โ”‚ โ”œโ”€โ”€ Cycle.js # Cycle CRUD operations โ”‚ โ””โ”€โ”€ Mortality.js # Mortality CRUD operations โ”œโ”€โ”€ routes/ โ”‚ โ”œโ”€โ”€ cycles.js # Cycle API routes โ”‚ โ””โ”€โ”€ mortality.js # Mortality API routes โ”œโ”€โ”€ server.js # Express app entry point โ”œโ”€โ”€ startup.sh # Startup script for Docker โ”œโ”€โ”€ package.json โ”œโ”€โ”€ .env # Environment configuration โ””โ”€โ”€ README.md ``` ## ๐Ÿ—„๏ธ Database ### Schema #### Table: cycles ```sql CREATE TABLE cycles ( id VARCHAR(50) PRIMARY KEY, total_days INTEGER NOT NULL, current_day INTEGER NOT NULL, start_date DATE NOT NULL, end_date DATE, chick_in_weight INTEGER, doc_in_count INTEGER, status VARCHAR(20) NOT NULL CHECK(status IN ('Completed', 'Active', 'Upcoming')), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` #### Table: mortality_records ```sql CREATE TABLE mortality_records ( id SERIAL PRIMARY KEY, cycle_id VARCHAR(50) NOT NULL, day INTEGER NOT NULL, mortality_count INTEGER NOT NULL DEFAULT 0, is_edited BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (cycle_id) REFERENCES cycles(id) ON DELETE CASCADE, UNIQUE(cycle_id, day) ); ``` ### Indexes ```sql CREATE INDEX idx_mortality_cycle_id ON mortality_records(cycle_id); CREATE INDEX idx_mortality_day ON mortality_records(day); CREATE INDEX idx_cycles_status ON cycles(status); CREATE INDEX idx_cycles_start_date ON cycles(start_date); ``` See complete schema in [database/schema.sql](database/schema.sql) ### Initial Data Database akan terisi otomatis dengan 4 siklus: | Cycle ID | Status | Start Date | End Date | DOC Count | | -------------------- | --------- | ---------- | ---------- | --------- | | CYCLE-JBW-2025-05-20 | Completed | 2025-05-20 | 2025-06-30 | 20,000 | | CYCLE-JBW-2025-07-11 | Completed | 2025-07-11 | 2025-08-21 | 20,000 | | CYCLE-JBW-2025-10-22 | Completed | 2025-10-22 | 2025-12-02 | 20,000 | | CYCLE-JBW-2025-12-10 | Active | 2025-12-10 | 2026-01-20 | 20,000 | ## ๐Ÿ“ก API Endpoints Base URL: `http://localhost:5001` ### Health Check ```http GET /health ``` Response: ```json { "status": "ok", "timestamp": "2025-12-22T03:02:20.793Z" } ``` ### Cycles API #### Get All Cycles ```http GET /api/cycles ``` Response: ```json { "success": true, "data": [ { "id": "CYCLE-JBW-2025-12-10", "totalDays": 42, "currentDay": 7, "startDate": "2025-12-10", "endDate": "2026-01-20", "chickInWeight": null, "docInCount": 20000, "status": "Active", "createdAt": "2025-12-22 02:59:17", "updatedAt": "2025-12-22 02:59:17" } ] } ``` #### Get Active Cycle ```http GET /api/cycles/active ``` #### Get Cycle by ID ```http GET /api/cycles/:id ``` Example: `GET /api/cycles/CYCLE-JBW-2025-12-10` #### Create Cycle ```http POST /api/cycles Content-Type: application/json { "id": "CYCLE-JBW-2025-12-10", "totalDays": 42, "currentDay": 0, "startDate": "2025-12-10", "endDate": "2026-01-20", "chickInWeight": 42, "docInCount": 20000, "status": "Active" } ``` #### Update Cycle ```http PUT /api/cycles/:id Content-Type: application/json { "totalDays": 42, "currentDay": 7, "startDate": "2025-12-10", "endDate": "2026-01-20", "chickInWeight": 42, "docInCount": 20000, "status": "Active" } ``` #### Delete Cycle ```http DELETE /api/cycles/:id ``` ### Mortality API #### Get All Mortality Records for Cycle ```http GET /api/mortality/:cycleId ``` Example: `GET /api/mortality/CYCLE-JBW-2025-12-10` Response: ```json { "success": true, "data": [ { "id": 1, "cycleId": "CYCLE-JBW-2025-12-10", "day": 0, "mortalityCount": 25, "isEdited": true, "createdAt": "2025-12-22 03:00:00", "updatedAt": "2025-12-22 03:00:00" } ] } ``` #### Get Mortality Record for Specific Day ```http GET /api/mortality/:cycleId/:day ``` Example: `GET /api/mortality/CYCLE-JBW-2025-12-10/5` #### Update/Create Mortality Record ```http PUT /api/mortality/:cycleId/:day Content-Type: application/json { "mortalityCount": 30 } ``` - Creates new record if doesn't exist - Updates existing record if exists - Sets `is_edited` flag to true #### Delete Mortality Record ```http DELETE /api/mortality/:cycleId/:day ``` Removes the mortality record, effectively resetting it to default value. ## ๐Ÿ” Environment Variables File: `.env` ```env PORT=5001 NODE_ENV=development # PostgreSQL Database Configuration DB_HOST=localhost DB_PORT=5432 DB_USER=dashboard_user DB_PASSWORD=your_secure_password_here DB_NAME=dashboard_db DB_SSL=false # Connection pool settings (optional) DB_POOL_MIN=2 DB_POOL_MAX=10 ``` ### Variables | Variable | Description | Default | | ----------- | ------------------------------------ | -------------- | | PORT | Server port | 5001 | | NODE_ENV | Environment (development/production) | development | | DB_HOST | PostgreSQL host | localhost | | DB_PORT | PostgreSQL port | 5432 | | DB_USER | PostgreSQL user | dashboard_user | | DB_PASSWORD | PostgreSQL password | (required) | | DB_NAME | Database name | dashboard_db | | DB_SSL | Enable SSL connection | false | | DB_POOL_MIN | Minimum pool connections | 2 | | DB_POOL_MAX | Maximum pool connections | 10 | ## ๐Ÿ”ง Development ### Database Helper Functions File: `database/db.js` ```javascript // Get connection pool and helpers const { pool, query, transaction, toISODate, parseISODate } = require('./database/db'); // Execute query const result = await query('SELECT * FROM cycles WHERE status = $1', ['Active']); // Run transaction await transaction(async (client) => { await client.query('UPDATE cycles SET current_day = $1 WHERE id = $2', [ 7, 'CYCLE-JBW-2025-12-10', ]); await client.query('INSERT INTO mortality_records ...'); }); // Date helpers toISODate(new Date()); // Converts Date to YYYY-MM-DD parseISODate('2025-12-10'); // Converts ISO string to Date ``` ### Models #### Cycle Model File: `models/Cycle.js` ```javascript const Cycle = require('./models/Cycle'); // Get all cycles Cycle.getAll(); // Get cycle by ID Cycle.getById('CYCLE-JBW-2025-12-10'); // Get active cycle Cycle.getActive(); // Create new cycle Cycle.create({ id: 'CYCLE-JBW-2025-12-10', totalDays: 42, currentDay: 0, startDate: '2025-12-10', endDate: '2026-01-20', docInCount: 20000, status: 'Active', }); // Update cycle Cycle.update('CYCLE-JBW-2025-12-10', { currentDay: 7, // ... other fields }); // Delete cycle Cycle.delete('CYCLE-JBW-2025-12-10'); ``` #### Mortality Model File: `models/Mortality.js` ```javascript const Mortality = require('./models/Mortality'); // Get all mortality records for a cycle Mortality.getByCycle('CYCLE-JBW-2025-12-10'); // Get mortality record for specific day Mortality.getByDay('CYCLE-JBW-2025-12-10', 5); // Insert or update mortality record Mortality.upsert('CYCLE-JBW-2025-12-10', 5, 30, true); // Delete mortality record Mortality.delete('CYCLE-JBW-2025-12-10', 5); ``` ### Request Logging All requests are logged with timestamp, method, and path: ``` 2025-12-22T03:02:20.793Z - GET /health 2025-12-22T03:02:22.884Z - GET /api/cycles 2025-12-22T03:02:24.985Z - GET /api/cycles/active ``` SQL queries are also logged in development mode. ### CORS Configuration File: `server.js` ```javascript const corsOptions = { origin: ['http://localhost:3001', 'http://localhost:5173'], methods: ['GET', 'POST', 'PUT', 'DELETE'], credentials: true, }; ``` Add more origins as needed for different environments. ## ๐Ÿงช Testing API ### Using curl ```bash # Health check curl http://localhost:5001/health # Get all cycles curl http://localhost:5001/api/cycles # Get active cycle curl http://localhost:5001/api/cycles/active # Get mortality records curl http://localhost:5001/api/mortality/CYCLE-JBW-2025-12-10 # Create mortality record curl -X PUT http://localhost:5001/api/mortality/CYCLE-JBW-2025-12-10/5 \ -H "Content-Type: application/json" \ -d '{"mortalityCount": 30}' # Delete mortality record curl -X DELETE http://localhost:5001/api/mortality/CYCLE-JBW-2025-12-10/5 ``` ### Using Postman or Thunder Client Import the following collection: ```json { "info": { "name": "Dashboard Peternakan API", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ { "name": "Health Check", "request": { "method": "GET", "url": "http://localhost:5001/health" } }, { "name": "Get All Cycles", "request": { "method": "GET", "url": "http://localhost:5001/api/cycles" } } ] } ``` ## ๐Ÿ“ Database Maintenance ### Backup Database **From Docker (Production):** ```bash # Run backup script (saves to ./backups/) ./scripts/backup-postgres.sh ``` **Fetch from Production Server:** ```bash REMOTE_USER=your-user \ REMOTE_HOST=your-host \ REMOTE_PROJECT_DIR=/path/to/project \ ./scripts/fetch-prod-db.sh ``` ### Restore Database **Restore to Local:** ```bash # This will reset local database and restore from backup ./scripts/restore-postgres-local.sh --reset backups/dashboard_db-YYYYMMDD-HHMMSS.sql.gz ``` ### Reset Database **Local Development:** ```bash npm run seed # Runs seed-postgres.js ``` **Docker:** ```bash docker-compose down -v # Remove volumes docker-compose up -d # Recreate with fresh data ``` ### View Database **Using psql CLI:** ```bash # Connect to local database psql -h localhost -p 5432 -U dashboard_user -d dashboard_db # SQL commands \dt # Show all tables \d cycles # Show table schema SELECT * FROM cycles; # Query data SELECT * FROM mortality_records; \q # Exit ``` **Using Docker:** ```bash docker-compose exec database psql -U dashboard_user -d dashboard_db ``` **Using DBeaver (GUI):** - Download from https://dbeaver.io/ - Connect to: localhost:5432 (or 15432 for Docker) - Database: dashboard_db - User/Password: from .env file ## ๐Ÿšจ Error Handling All endpoints return consistent error format: ```json { "success": false, "error": "Error message here" } ``` HTTP Status Codes: - `200` - Success - `201` - Created - `400` - Bad Request (invalid input) - `404` - Not Found - `500` - Internal Server Error ## ๐Ÿ”’ Security Notes - Database credentials should be kept in .env (gitignored) - Foreign keys enforce referential integrity - SQL injection is prevented by using parameterized queries ($1, $2, etc.) - CORS is configured for specific origins only - Input validation on all endpoints - Connection pooling manages database connections efficiently - SSL can be enabled for production (set DB_SSL=true) ## ๐Ÿ“š Additional Resources - [Express.js Documentation](https://expressjs.com/) - [node-postgres (pg) Documentation](https://node-postgres.com/) - [PostgreSQL Documentation](https://www.postgresql.org/docs/) - [Local Database Setup Guide](../docs/LOCAL_DATABASE_GUIDE.md) ## ๐Ÿค Contributing When contributing to the backend: 1. Follow existing code structure 2. Add error handling for new endpoints 3. Update this README if adding new features 4. Test all endpoints before committing 5. Keep models thin - business logic in models, HTTP in routes ## ๐Ÿ“ž Support For backend-specific issues: - Check server logs in console - Verify PostgreSQL is running: `pg_isready` or `docker-compose ps` - Test database connection: `npm run seed` - Check port availability: `lsof -i :5001` - Verify .env configuration (DB_HOST, DB_PORT, credentials) --- **Backend developed by PT Cipta Pola Solusi Prima - 2025**