Rest API Waste Management
π Waste Management Prediction API - Complete Technical Documentation
Advanced REST API untuk Sistem Prediksi Limbah (Waste Management) menggunakan Next.js 16, Prisma ORM, dan TiDB Database. Dibangun untuk kompetisi dengan fokus pada performa, skalabilitas, dan akurasi prediksi berbasis AI.
π Table of Contents
- Project Overview
- Technology Stack
- Project Architecture
- Folder Structure
- Database Schema
- API Endpoints
- Code Implementation
- Setup & Installation
- Development Guide
- Deployment
π― Project Overview
Waste Management Prediction API adalah sistem backend yang dirancang untuk memprediksi volume limbah berdasarkan lokasi dan jumlah pengunjung di berbagai area (JIS, GBK, Stadiun, dll).
Fitur Utama:
β
AI-Driven Waste Prediction - Integrasi AI service untuk estimasi volume sampah
β
Dynamic Logistics Calculation - Perhitungan otomatis kebutuhan truk, personel, dan jam kerja
β
TiDB Integration - Database MySQL-compatible yang terukur dan reliable
β
Prisma ORM - Type-safe database queries dengan TypeScript
β
Next.js API Routes - REST API dengan App Router modern
β
Hybrid Logic System - Mode AI untuk lokasi terdaftar, fallback untuk lokasi baru
β
Spatial Data Support - Koordinat GPS untuk integrasi peta
β
Production Ready - Error handling, logging, dan best practices
Use Cases:
π Dinas Lingkungan Hidup (DLH) - Prediksi volume limbah per lokasi
ποΈ Event Management - Estimasi sampah untuk acara besar
π Fleet Planning - Optimasi armada dan personel operasional
π Data Analytics - Historical logs untuk trend analysis
π οΈ Technology Stack
| Layer | Technology | Version | Purpose |
|---|---|---|---|
| Runtime | Node.js | 18+ | JavaScript runtime |
| Framework | Next.js | 16.2.6 | React + API routes |
| Language | TypeScript | 5 | Type safety |
| Database | TiDB (MySQL) | Latest | Data persistence |
| ORM | Prisma | 5.22.0 | Database abstraction |
| Styling | Tailwind CSS | 4 | UI components (optional) |
| Linting | ESLint | 9 | Code quality |
| Package Manager | npm | 10+ | Dependency management |
| Deployment | Vercel | Latest | Serverless hosting |
Dependencies Breakdown:
Production Dependencies:
{
"@prisma/client": "^5.22.0", // Database client
"dotenv": "^17.4.2", // Environment variables
"next": "16.2.6", // Web framework
"react": "19.2.4", // UI library
"react-dom": "19.2.4" // React DOM
}
Development Dependencies:
{
"@tailwindcss/postcss": "^4", // CSS framework
"@types/*": "^20/19", // TypeScript definitions
"eslint": "^9", // Linter
"eslint-config-next": "16.2.6", // Next.js linting rules
"prisma": "^5.22.0", // Migration tools
"typescript": "^5" // TypeScript compiler
}
ποΈ Project Architecture
System Flow Diagram
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Client/Frontend β
β (External Application) β
ββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
POST /api/waste
(JSON Payload)
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Next.js API Route Handler β
β (app/api/waste/route.ts - POST Method) β
β β
β 1. Validate Input (nama_lokasi) β
β 2. Call AI Service (Hugging Face / External API) β
β 3. Process Results β
β 4. Query/Create Area in Database β
β 5. Log Prediction to PredictionLog β
β 6. Format & Return JSON Response β
ββββββββββββββββββββ¬ββββββββββββββββββββββββ¬βββββββββββββββββββ
β β
ββββββββββββΌβββββββββββ ββββββββββΌββββββββββ
β β β β
β AI Service API β β TiDB Database β
β (Prediction) β β (Prisma Client) β
β β β β
β - Volume Ton β β - MasterArea β
β - Food Waste Ton β β - PredictionLog β
β - Plastic Ton β β - FleetInventory β
β - Confidence β β - TPA Facility β
β β β β
βββββββββββββββββββββββ ββββββββββββββββββββ
Data Flow:
- Input Validation β Memastikan
nama_lokasiada - AI Integration β Memanggil AI service untuk prediksi
- Database Query β Cari area di MasterArea table
- Data Creation β Buat entry baru jika area belum terdaftar
- Log Storage β Simpan hasil prediksi ke PredictionLog
- Response Formatting β Format dan return JSON response
π Folder Structure
rest_api/
βββ π app/ # Next.js App Router directory
β βββ π api/ # API Routes (Next.js 13+)
β β βββ π waste/ # Waste prediction endpoint
β β βββ route.ts # POST /api/waste handler
β β
β βββ π lib/ # Utility & Library Files
β β βββ prisma.ts # Prisma client singleton
β β βββ waste-api.ts # AI Service integration
β β
β βββ favicon.ico # Website favicon
β βββ globals.css # Global styles
β βββ layout.tsx # Root layout component
β βββ page.tsx # Home page (GET /)
β
βββ π prisma/ # Prisma ORM Configuration
β βββ schema.prisma # Database schema definition
β
βββ π public/ # Static assets
β βββ (images, icons, etc.)
β
βββ π package.json # Project dependencies
βββ π tsconfig.json # TypeScript configuration
βββ π next.config.ts # Next.js configuration
βββ π eslint.config.mjs # ESLint rules
βββ π postcss.config.mjs # PostCSS configuration
βββ π .env.example # Environment template
βββ π .gitignore # Git ignore rules
βββ π README.md # Project documentation
Detailed Folder Explanation:
π app/ - Next.js App Router
Direktori utama Next.js 13+ untuk routes dan layouts.
- Menggunakan file-based routing
- Supports Server Components (default)
- Supports API Routes dengan route.ts
π app/api/ - API Routes
Lokasi endpoint REST API.
- Menggunakan struktur folder sesuai URL path
- Setiap folder dengan route.ts = 1 endpoint
- Contoh: app/api/waste/route.ts β POST /api/waste
π app/lib/ - Utility Functions
Reusable utility dan service functions:
- prisma.ts β Singleton Prisma client
- waste-api.ts β AI service integration
π prisma/ - Database Configuration
Prisma ORM configuration dan schema:
- schema.prisma β Database models & relationships
- migrations/ β Database version history (auto-generated)
π Database Schema
Models Overview
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Database Models β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββ MasterArea ββββββββββββ ββ PredictionLog β β
β β id (PK) βββββ€ id (PK) β β
β β name (String, UNIQUE) β β areaId (FK) βββ β
β β latitude (Float) β β prediction_date β
β β longitude (Float) β β volume_ton β
β β [relationships] β β confidence_score β
β β - predictions (1:N) β β risk_status β
β β - permits (1:N) β β created_at β
β ββββββββββββββββββββββββββββ ββββββββββββββββββββββ
β
β ββββ CrowdPermit βββββββββββ ββ TpaFacility βββ β
β β id (PK) β β id (PK) β β
β β areaId (FK) βββββ β β name (String) β β
β β event_name ββββββββββ β max_capacity_ β β
β β event_date β β ton (Float) β β
β β estimated_crowd β β current_load_ β β
β β status β β ton (Float) β β
β ββββββββββββββββββββββββββββ ββββββββββββββββββ β
β
β ββ OperationalParam βββ ββ FleetInventory βββ β
β β param_key (PK) β β id (PK) β β
β β param_value (Float) β β truck_type β β
β βββββββββββββββββββββββ β total_units β β
β β ready_units β β
β β capacity_per_truck β β
β ββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Model Details:
1οΈβ£ MasterArea - Daftar Lokasi/Area
model MasterArea {
id Int @id @default(autoincrement())
name String @unique
latitude Float
longitude Float
predictions PredictionLog[]
permits CrowdPermit[]
}
Purpose: Menyimpan daftar area/lokasi yang bisa diprediksi
Fields:
id- Auto-increment primary keyname- Nama unik area (contoh: βjisβ, βgbkβ)latitude- Koordinat lintang untuk pemetaanlongitude- Koordinat bujur untuk pemetaanpredictions- Relasi ke PredictionLog (1:N)permits- Relasi ke CrowdPermit (1:N)
Example Data:
{
"id": 1,
"name": "jis",
"latitude": -6.1214,
"longitude": 106.8830
}
2οΈβ£ PredictionLog - Riwayat Prediksi
model PredictionLog {
id Int @id @default(autoincrement())
areaId Int
area MasterArea @relation(fields: [areaId], references: [id])
prediction_date DateTime
volume_ton Float
confidence_score Float
risk_status String
created_at DateTime @default(now())
}
Purpose: Menyimpan setiap prediksi yang dilakukan untuk audit trail
Fields:
id- Auto-increment primary keyareaId- Foreign key ke MasterAreaprediction_date- Tanggal prediksi dilakukanvolume_ton- Total volume limbah (ton)confidence_score- Akurasi prediksi AI (0-100 atau 0-1)risk_status- Level risiko (HIGH, MEDIUM, LOW)created_at- Timestamp otomatis saat dibuat
Example Data:
{
"id": 1,
"areaId": 1,
"prediction_date": "2026-05-12T10:30:45Z",
"volume_ton": 12.5,
"confidence_score": 0.85,
"risk_status": "MEDIUM",
"created_at": "2026-05-12T10:30:45Z"
}
3οΈβ£ CrowdPermit - Izin Event Keramaian
model CrowdPermit {
id Int @id @default(autoincrement())
areaId Int
area MasterArea @relation(fields: [areaId], references: [id])
event_name String
event_date DateTime
estimated_crowd Int
status String @default("PENDING")
}
Purpose: Tracking event khusus dan estimasi keramaian
Fields:
id- Auto-increment primary keyareaId- Foreign key ke MasterAreaevent_name- Nama event/acaraevent_date- Tanggal event berlangsungestimated_crowd- Estimasi jumlah pengunjungstatus- Status izin (PENDING, APPROVED, REJECTED)
4οΈβ£ TpaFacility - Fasilitas TPA (Tempat Pembuangan Akhir)
model TpaFacility {
id Int @id @default(autoincrement())
name String
max_capacity_ton Float
current_load_ton Float
}
Purpose: Data kapasitas TPA untuk memastikan tidak overload
Fields:
id- Auto-increment primary keyname- Nama TPAmax_capacity_ton- Kapasitas maksimal (ton)current_load_ton- Beban saat ini (ton)
5οΈβ£ OperationalParam - Parameter Operasional
model OperationalParam {
param_key String @id
param_value Float
}
Purpose: Menyimpan konstanta operasional yang bisa diubah
Fields:
param_key- Nama parameter (contoh: βCAPACITY_PER_TRUCKβ)param_value- Nilai float dari parameter
Contoh Nilai:
CAPACITY_PER_TRUCK = 5.0 // 5 mΒ³ per truk
WEIGHT_CONVERSION = 0.4 // 1 mΒ³ = 0.4 ton
STAFF_PER_TRUCK = 3 // 3 orang per truk
SHIFT_HOURS = 8 // 8 jam kerja
6οΈβ£ FleetInventory - Inventaris Armada
model FleetInventory {
id Int @id @default(autoincrement())
truck_type String
total_units Int
ready_units Int
capacity_per_truck_ton Float
}
Purpose: Tracking armada truk yang tersedia
Fields:
id- Auto-increment primary keytruck_type- Tipe truk (Compactor, Dump Truck, dll)total_units- Total unit yang adaready_units- Unit siap operasionalcapacity_per_truck_ton- Kapasitas per truk (ton)
π API Endpoints
Endpoint Overview
| Method | Endpoint | Description | Auth | Status |
|---|---|---|---|---|
GET | / | Home page info | β | β Active |
POST | /api/waste | Waste prediction | β | β Active |
POST /api/waste - Waste Prediction
Request Format
POST /api/waste HTTP/1.1
Host: your-api.vercel.app
Content-Type: application/json
Request Body
{
"nama_lokasi": "JIS",
"visitor_count": 80000,
"event_name": "Concert XYZ"
}
Required Fields:
| Field | Type | Description |
|---|---|---|
nama_lokasi | String | Nama lokasi (Required) |
visitor_count | Integer | Estimasi pengunjung (Optional) |
event_name | String | Nama event (Optional) |
Response Format (Success - 200 OK)
{
"status": "success",
"message": "Prediction completed successfully",
"confidence_score": 0.85,
"generated_at": "2026-05-12T10:30:45.123Z",
"data": {
"location": "JIS",
"is_area_registered": true,
"coordinates": {
"lat": -6.1214,
"lng": 106.8830
},
"waste_summary": {
"total_volume_ton": 12.5,
"total_food_waste_ton": 8.3,
"total_plastic_ton": 2.1
},
"prediction_results": [
{
"tanggal": "2026-05-12",
"lokasi": "JIS",
"total_volume_ton": 12.5,
"sisa_makanan_ton": 8.3,
"plastik_ton": 2.1,
"rekomendasi_truk": 3,
"calculated_staff": 9,
"man_hours": 72,
"weight_kg": 12500,
"status_risiko": "SAFE β
",
"info_event": "Concert XYZ"
}
],
"logistics_plan": {},
"database_log": {
"id": 1,
"areaId": 1,
"prediction_date": "2026-05-12T10:30:45.123Z",
"volume_ton": 12.5,
"confidence_score": 0.85,
"risk_status": "MEDIUM",
"created_at": "2026-05-12T10:30:45.123Z"
}
}
}
Response Format (Error - 400/500)
{
"status": "error",
"message": "Properti 'nama_lokasi' wajib diisi."
}
HTTP Status Codes
| Code | Meaning | Scenario |
|---|---|---|
| 200 | Success | Prediksi berhasil dilakukan |
| 400 | Bad Request | nama_lokasi tidak diisi |
| 500 | Server Error | Error di AI service atau database |
cURL Examples
Success Request:
curl -X POST http://localhost:3000/api/waste \
-H "Content-Type: application/json" \
-d '{
"nama_lokasi": "JIS",
"visitor_count": 80000
}'
Error Request (Missing Field):
curl -X POST http://localhost:3000/api/waste \
-H "Content-Type: application/json" \
-d '{
"visitor_count": 80000
}'
π» Code Implementation
1οΈβ£ app/lib/prisma.ts - Database Client
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma =
globalForPrisma.prisma ??
new PrismaClient();
if (process.env.NODE_ENV !== 'production')
globalForPrisma.prisma = prisma;
export default prisma;
Purpose: Singleton pattern untuk Prisma Client
Why Singleton?
- Menghindari multiple database connections
- Reuse koneksi yang sama across requests
- Lebih efficient di serverless environment
Penggunaan:
import { prisma } from "@/app/lib/prisma";
// Query database
const area = await prisma.masterArea.findFirst({...});
2οΈβ£ app/lib/waste-api.ts - AI Service Integration
class WastePredictionService {
private static instance: WastePredictionService;
private readonly apiUrl: string;
private constructor() {
this.apiUrl = process.env.API_URL || "";
}
public static getInstance(): WastePredictionService {
if (!WastePredictionService.instance) {
WastePredictionService.instance = new WastePredictionService();
}
return WastePredictionService.instance;
}
async predict(payload: Record<string, any>) {
const response = await fetch(this.apiUrl, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload),
cache: "no-store",
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`AI API Error: ${response.status} - ${errorText}`);
}
return response.json();
}
}
export const wasteService = WastePredictionService.getInstance();
Pattern: Singleton Design Pattern
Features:
- Private constructor mencegah multiple instances
- Static getInstance() mengembalikan instance yang sama
- Reusable across entire application
Penggunaan:
import { wasteService } from "@/app/lib/waste-api";
// Call AI service
const aiData = await wasteService.predict({
nama_lokasi: "JIS",
visitor_count: 80000
});
3οΈβ£ app/api/waste/route.ts - Main API Handler
A. Header & Setup
export const dynamic = 'force-dynamic';
import { NextResponse } from "next/server";
import { wasteService } from "../../lib/waste-api";
import { prisma } from "../../lib/prisma";
const DEFAULT_COORDINATES = { lat: -6.2088, lng: 106.8456 };
export const dynamic = 'force-dynamic' - Force dynamic rendering (tidak cache)
B. Handler Function
export async function POST(req: Request) {
try {
// Step 1: Parse request body
const body = await req.json().catch(() => ({}));
// Step 2: Validate input
if (!body.nama_lokasi) {
return NextResponse.json(
{ status: "error", message: "Properti 'nama_lokasi' wajib diisi." },
{ status: 400 }
);
}
// Step 3: Call AI service
const aiData = await wasteService.predict(body);
// Step 4: Extract location name
const aiLocationName = aiData.data?.prediction_results?.[0]?.lokasi
|| body.nama_lokasi
|| "Unknown";
const normalizedLocation = aiLocationName.toLowerCase().trim();
// Step 5: Query or create area
let areaData = await prisma.masterArea.findFirst({
where: {
name: {
equals: normalizedLocation
}
},
select: {
id: true,
latitude: true,
longitude: true
}
});
if (!areaData) {
areaData = await prisma.masterArea.create({
data: {
name: aiLocationName,
latitude: DEFAULT_COORDINATES.lat,
longitude: DEFAULT_COORDINATES.lng
},
select: {
id: true,
latitude: true,
longitude: true
}
});
}
// Step 6: Calculate totals
const predictionResults = aiData.data?.prediction_results || [];
const totalVolume = predictionResults.reduce(
(acc: number, item: any) => acc + (item.total_volume_ton || 0),
0
);
const totalFoodWaste = predictionResults.reduce(
(acc: number, item: any) => acc + (item.sisa_makanan_ton || 0),
0
);
const totalPlastic = predictionResults.reduce(
(acc: number, item: any) => acc + (item.plastik_ton || 0),
0
);
// Step 7: Determine risk status
const riskStatus = totalVolume > 10
? "HIGH"
: totalVolume > 5
? "MEDIUM"
: "LOW";
// Step 8: Log to database
const dbLog = await prisma.predictionLog.create({
data: {
areaId: areaData.id,
prediction_date: new Date(),
volume_ton: totalVolume,
confidence_score: aiData.confidence_score
? parseFloat(aiData.confidence_score.toString().replace('%', ''))
: 0,
risk_status: riskStatus
},
});
// Step 9: Format response
const enrichedResults = predictionResults.map((item: any) => {
const vol = item.total_volume_ton || 0;
return {
tanggal: item.tanggal,
lokasi: item.lokasi,
total_volume_ton: vol,
sisa_makanan_ton: item.sisa_makanan_ton || 0,
plastik_ton: item.plastik_ton || 0,
rekomendasi_truk: item.rekomendasi_truk || Math.ceil(vol / 5),
calculated_staff: item.calculated_staff || Math.ceil((vol / 5) * 3),
man_hours: item.man_hours || Math.ceil((vol / 5) * 3 * 8),
status_risiko: item.status_risiko || riskStatus,
info_event: item.info_event || "Tidak ada Event"
};
});
// Step 10: Get coordinates
const dynamicCoordinates = {
lat: Number(areaData.latitude),
lng: Number(areaData.longitude)
};
// Step 11: Return success response
return NextResponse.json({
status: aiData.status || "success",
message: aiData.message,
confidence_score: aiData.confidence_score ? parseFloat(aiData.confidence_score.toString()) : 0,
generated_at: new Date().toISOString(),
data: {
location: aiLocationName,
is_area_registered: true,
coordinates: dynamicCoordinates,
waste_summary: {
total_volume_ton: totalVolume,
total_food_waste_ton: totalFoodWaste,
total_plastic_ton: totalPlastic,
},
prediction_results: enrichedResults,
logistics_plan: aiData.data?.logistics_plan || {},
database_log: dbLog,
},
});
} catch (error: any) {
console.error("API Error:", error.message);
return NextResponse.json(
{ status: "error", message: error.message },
{ status: 500 },
);
}
}
Key Points:
- Input validation dengan error handling
- AI service integration dengan try-catch
- Database query dengan Prisma
- Risk status calculation based on volume
- Comprehensive logging ke database
- Formatted JSON response
4οΈβ£ app/page.tsx - Home Page
export default function Home() {
return (
<div style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
<h1>π Waste Prediction API</h1>
<p>Backend API sudah berjalan dengan baik.</p>
<p>Silakan akses endpoint <code>/api/waste</code>
menggunakan metode POST untuk mendapatkan data prediksi.</p>
</div>
);
}
Purpose: Simple landing page untuk test server
π Setup & Installation
Prerequisites
# Node.js 18+
node --version
# npm 10+
npm --version
# Git
git --version
Step 1: Clone Repository
git clone https://github.com/BagasHtml/rest_api.git
cd rest_api
Step 2: Install Dependencies
npm install
Ini akan install:
- Next.js & React
- Prisma & Database client
- TypeScript
- ESLint & Dev tools
- Tailwind CSS
Step 3: Environment Setup
# Copy environment file
cp .env.example .env
# Edit .env dengan credentials Anda
nano .env
Environment Variables:
# Database
DATABASE_URL="mysql://user:password@host:port/database"
# AI Service
API_URL="https://your-ai-service-api-endpoint"
# Node Environment
NODE_ENV="development"
Step 4: Initialize Database
# Generate Prisma client
npx prisma generate
# Create database & run migrations
npx prisma migrate dev --name init
# (Optional) Seed database dengan data sample
npx prisma db seed
Step 5: Development Server
# Start development server
npm run dev
# Server berjalan di: http://localhost:3000
# API endpoint: http://localhost:3000/api/waste
Step 6: Testing
# Test dengan cURL
curl -X POST http://localhost:3000/api/waste \
-H "Content-Type: application/json" \
-d '{"nama_lokasi": "JIS", "visitor_count": 80000}'
# Test dengan Postman
# 1. Create new POST request
# 2. URL: http://localhost:3000/api/waste
# 3. Body (JSON): {"nama_lokasi": "JIS"}
# 4. Send
π Development Guide
Code Style & Standards
TypeScript Best Practices:
// β
Good - Type everything
interface PredictionRequest {
nama_lokasi: string;
visitor_count?: number;
event_name?: string;
}
async function handlePrediction(
body: PredictionRequest
): Promise<PredictionResponse> {
// ...
}
// β Bad - Avoid any type
async function handlePrediction(body: any): Promise<any> {
// ...
}
Prisma Best Practices:
// β
Good - Select specific fields
const area = await prisma.masterArea.findFirst({
where: { name: location },
select: { id: true, latitude: true, longitude: true }
});
// β Bad - Select all fields
const area = await prisma.masterArea.findFirst({
where: { name: location }
});
Error Handling:
// β
Good - Proper error handling
try {
const data = await aiService.predict(payload);
return NextResponse.json({ status: 'success', data });
} catch (error) {
console.error('Prediction error:', error);
return NextResponse.json(
{ status: 'error', message: error.message },
{ status: 500 }
);
}
// β Bad - Silent failures
const data = await aiService.predict(payload);
return NextResponse.json({ data });
Adding New Endpoints
Example: Create GET /api/areas endpoint
- Create folder structure:
app/api/areas/
βββ route.ts
- Implement handler:
// app/api/areas/route.ts
import { NextResponse } from "next/server";
import { prisma } from "@/app/lib/prisma";
export async function GET() {
try {
const areas = await prisma.masterArea.findMany({
select: {
id: true,
name: true,
latitude: true,
longitude: true
}
});
return NextResponse.json({
status: 'success',
data: areas
});
} catch (error: any) {
return NextResponse.json(
{ status: 'error', message: error.message },
{ status: 500 }
);
}
}
- Test:
curl http://localhost:3000/api/areas
Modifying Database Schema
- Edit prisma/schema.prisma:
model NewModel {
id Int @id @default(autoincrement())
name String
}
- Create migration:
npx prisma migrate dev --name add_new_model
- Apply changes:
npx prisma generate
π Deployment
Deploy to Vercel (Recommended)
Step 1: Push to GitHub
git push origin main
Step 2: Connect to Vercel
1. Visit vercel.com
2. Import repository
3. Select project folder: rest_api
4. Configure environment variables
5. Deploy
Step 3: Set Environment Variables
In Vercel Dashboard:
DATABASE_URL = your_tidb_connection_string
API_URL = your_ai_service_endpoint
NODE_ENV = production
Step 4: Run Database Migrations
# Via Vercel CLI
vercel env pull
npx prisma migrate deploy
Alternative: Docker Deployment
Dockerfile:
FROM node:18-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source
COPY . .
# Generate Prisma client
RUN npx prisma generate
# Build app
RUN npm run build
# Expose port
EXPOSE 3000
# Start server
CMD ["npm", "start"]
Build & Run:
docker build -t waste-api .
docker run -p 3000:3000 -e DATABASE_URL="..." waste-api
π Additional Resources
Documentation Links:
- Next.js 16 - https://nextjs.org/docs
- Prisma ORM - https://www.prisma.io/docs
- TypeScript - https://www.typescriptlang.org/docs
- TiDB - https://docs.pingcap.com/tidb
- Vercel - https://vercel.com/docs
Frontend Integration:
// Example: React component to call API
async function predictWaste(locationName: string) {
const response = await fetch('/api/waste', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ nama_lokasi: locationName })
});
const result = await response.json();
return result;
}
β Checklist Sebelum Kompetisi
- Semua environment variables sudah diset
- Database sudah terkoneksi dan migrate
- API endpoint sudah tested dengan Postman
- Error handling sudah comprehensive
- TypeScript tidak ada error
- ESLint rules sudah dipenuhi
- README dokumentasi lengkap
- Deployment ke Vercel berhasil
- Database backup sudah ada
- API response sesuai spesifikasi
π― Performance Tips
1. Database Query Optimization
// β
Good - Specific select
const area = await prisma.masterArea.findFirst({
where: { name: location },
select: { id: true, latitude: true } // Only needed fields
});
// β Bad - Select all
const area = await prisma.masterArea.findFirst({
where: { name: location }
});
2. Caching Strategy
// Add caching headers untuk GET requests
export async function GET() {
const response = NextResponse.json(data);
response.headers.set('Cache-Control', 'public, s-maxage=60');
return response;
}
3. Pagination untuk Large Results
// Implement pagination
const areas = await prisma.masterArea.findMany({
skip: (page - 1) * 20,
take: 20
});