# Elysia API Documentation ## Overview This project uses ElysiaJS integrated with Next.js App Router to create high-performance, type-safe APIs. The codebase follows a **Feature-based MVC pattern** for maintainability and scalability. ## Base URL ``` http://localhost:3000 ``` ## Endpoints ### Customers API #### Get All Customers by Branch ``` GET /api/customers/:branch ``` **Parameters:** - `branch` (path parameter, required): Branch identifier - Examples: `branch-01`, `branch-02`, `head-office` - `status` (query parameter, optional): Filter by customer status - Values: `active`, `inactive`, `pending` **Examples:** 1. Get all customers from branch-01: ```bash curl http://localhost:3000/api/customers/branch-01 ``` 2. Get active customers from branch-02: ```bash curl "http://localhost:3000/api/customers/branch-02?status=active" ``` 3. Get pending customers from head-office: ```bash curl "http://localhost:3000/api/customers/head-office?status=pending" ``` **Response Format:** ```json { "success": true, "data": [ { "id": "cust-001", "branch": "branch-01", "name": "สมชาย ใจดี", "email": "somchai@example.com", "phone": "081-234-5678", "company": "บริษัท ไทยธุรกิจ จำกัด", "address": "123 ถนนสุขุมวิท แขวงคลองตัน เขตคลองเตย กรุงเทพฯ 10110", "status": "active", "createdAt": "2024-01-15T09:00:00Z", "updatedAt": "2024-01-15T09:00:00Z" } ], "count": 1, "message": "Found 1 customer(s) for branch: branch-01" } ``` #### Get Single Customer by ID ``` GET /api/customers/:branch/:id ``` #### Create Customer ``` POST /api/customers ``` #### Update Customer ``` PUT /api/customers/:branch/:id ``` #### Delete Customer ``` DELETE /api/customers/:branch/:id ``` --- ### Quotations API #### Get All Quotations by Branch ``` GET /api/quotations/:branch ``` **Parameters:** - `branch` (path parameter, required): Branch identifier - Examples: `branch-01`, `branch-02`, `head-office` - `status` (query parameter, optional): Filter by quotation status - Values: `draft`, `sent`, `accepted`, `rejected`, `expired` **Examples:** 1. Get all quotations from branch-01: ```bash curl http://localhost:3000/api/quotations/branch-01 ``` 2. Get sent quotations from head-office: ```bash curl "http://localhost:3000/api/quotations/head-office?status=sent" ``` **Response Format:** ```json { "success": true, "data": [ { "id": "quot-001", "quotationNumber": "QT-2024-001", "branch": "branch-01", "customerId": "cust-001", "customerName": "สมชาย ใจดี", "date": "2024-01-20T00:00:00Z", "validUntil": "2024-02-20T00:00:00Z", "subtotal": 50000, "taxRate": 0.07, "taxAmount": 3500, "totalAmount": 53500, "status": "sent", "notes": "Quotation for office supplies", "createdAt": "2024-01-20T09:00:00Z", "updatedAt": "2024-01-20T09:00:00Z" } ], "count": 2, "message": "Found 2 quotation(s) for branch: branch-01" } ``` #### Get Single Quotation by ID ``` GET /api/quotations/:branch/:id ``` #### Create Quotation ``` POST /api/quotations ``` #### Update Quotation ``` PUT /api/quotations/:branch/:id ``` #### Delete Quotation ``` DELETE /api/quotations/:branch/:id ``` --- ### Master Options API #### Get All Master Options ``` GET /api/master-options ``` **Query Parameters:** - `category` (optional): Filter by category (e.g., `customer_type`, `payment_method`, `industry`) - `isActive` (optional): Filter by active status (`true` or `false`) - `search` (optional): Search in code, nameTh, or nameEn **Example:** ```bash curl "http://localhost:3000/api/master-options?category=customer_type&isActive=true" ``` **Response Format:** ```json { "success": true, "data": [ { "id": "opt-001", "branchId": "branch-01", "category": "customer_type", "code": "CORPORATE", "nameTh": "องค์กร/บริษัท", "nameEn": "Corporate", "descriptionTh": "ลูกค้าประเภทองค์กร", "descriptionEn": "Corporate customers", "isActive": true, "createdAt": "2024-01-15T09:00:00Z", "updatedAt": "2024-01-15T09:00:00Z" } ], "count": 1, "message": "Found 1 master option(s)" } ``` #### Get Single Master Option ``` GET /api/master-options/:id ``` #### Create Master Option ``` POST /api/master-options ``` **Request Body:** ```json { "category": "customer_type", "code": "INDIVIDUAL", "nameTh": "บุคคลธรรมดา", "nameEn": "Individual", "descriptionTh": "ลูกค้ารายบุคคล", "descriptionEn": "Individual customers" } ``` #### Update Master Option ``` PUT /api/master-options/:id ``` #### Delete Master Option ``` DELETE /api/master-options/:id ``` #### Toggle Active Status ``` PATCH /api/master-options/:id/toggle ``` --- ### Locations API #### Get All Locations ``` GET /api/locations ``` **Query Parameters:** - `type` (optional): Filter by location type (`country`, `province`, `district`, `subdistrict`) - `parentId` (optional): Filter by parent location ID - `search` (optional): Search in code, nameTh, or nameEn - `isActive` (optional): Filter by active status **Example:** ```bash curl "http://localhost:3000/api/locations?type=province&search=กรุงเทพ" ``` **Response Format:** ```json { "success": true, "data": [ { "id": "loc-001", "branchId": "head-office", "code": "TH-10", "nameTh": "กรุงเทพมหานคร", "nameEn": "Bangkok", "type": "province", "parentId": "country-th-id", "isActive": true, "createdAt": "2024-01-15T09:00:00Z", "updatedAt": "2024-01-15T09:00:00Z" } ], "count": 1, "message": "Found 1 location(s)" } ``` #### Get Locations by Type ``` GET /api/locations/type/:type ``` **Parameters:** - `type` (path parameter): `country`, `province`, `district`, or `subdistrict` **Example:** ```bash curl http://localhost:3000/api/locations/type/province ``` #### Get Single Location ``` GET /api/locations/:id ``` #### Create Location ``` POST /api/locations ``` **Request Body:** ```json { "code": "TH-10", "nameTh": "กรุงเทพมหานคร", "nameEn": "Bangkok", "type": "province", "parentId": "country-th-id" } ``` #### Update Location ``` PUT /api/locations/:id ``` #### Delete Location ``` DELETE /api/locations/:id ``` #### Toggle Active Status ``` PATCH /api/locations/:id/toggle ``` --- ### Industrial Estates API #### Get All Industrial Estates ``` GET /api/industrial-estates ``` **Query Parameters:** - `locationId` (optional): Filter by location ID - `isActive` (optional): Filter by active status - `search` (optional): Search in code, nameTh, or nameEn **Example:** ```bash curl "http://localhost:3000/api/industrial-estates?locationId=th-10&isActive=true" ``` **Response Format:** ```json { "success": true, "data": [ { "id": "ie-001", "branchId": "head-office", "code": "BPL", "nameTh": "นิคมอุตสาหกรรมบางพลี", "nameEn": "Bangpoo Industrial Estate", "locationId": "th-10", "latitude": 13.5991, "longitude": 100.7015, "isActive": true, "createdAt": "2024-01-15T09:00:00Z", "updatedAt": "2024-01-15T09:00:00Z" } ], "count": 1, "message": "Found 1 industrial estate(s)" } ``` #### Get Industrial Estates by Location ``` GET /api/industrial-estates/location/:locationId ``` **Example:** ```bash curl http://localhost:3000/api/industrial-estates/location/th-10 ``` #### Get Single Industrial Estate ``` GET /api/industrial-estates/:id ``` #### Create Industrial Estate ``` POST /api/industrial-estates ``` **Request Body:** ```json { "code": "BPL", "nameTh": "นิคมอุตสาหกรรมบางพลี", "nameEn": "Bangpoo Industrial Estate", "locationId": "th-10", "latitude": 13.5991, "longitude": 100.7015 } ``` #### Update Industrial Estate ``` PUT /api/industrial-estates/:id ``` #### Delete Industrial Estate ``` DELETE /api/industrial-estates/:id ``` #### Toggle Active Status ``` PATCH /api/industrial-estates/:id/toggle ``` --- ### Audit Logs API **Note:** This API requires Admin/Superadmin/Auditor access level. #### Get All Audit Logs ``` GET /api/audit-logs ``` **Query Parameters:** - `startDate` (optional): Filter logs from this date (ISO 8601 format) - `endDate` (optional): Filter logs until this date (ISO 8601 format) - `action` (optional): Filter by action type (`CREATE`, `UPDATE`, `DELETE`, `READ`) - `entityType` (optional): Filter by entity type (`customer`, `quotation`, `location`, etc.) - `limit` (optional): Number of results to return (default: 50) - `offset` (optional): Number of results to skip (for pagination) **Example:** ```bash curl "http://localhost:3000/api/audit-logs?action=CREATE&limit=10" ``` **Response Format:** ```json { "success": true, "data": [ { "id": "audit-001", "branchId": "branch-01", "userId": "user-123", "actorId": "user-123", "entityType": "customer", "entityId": "cust-001", "action": "CREATE", "actionTh": "สร้าง", "oldValues": null, "newValues": { "name": "สมชาย ใจดี", "email": "somchai@example.com" }, "ipAddress": "192.168.1.100", "userAgent": "Mozilla/5.0...", "createdAt": "2024-01-15T09:00:00Z" } ], "count": 1, "message": "Found 1 audit log(s)" } ``` #### Get Audit Log Statistics ``` GET /api/audit-logs/stats ``` **Response Format:** ```json { "success": true, "data": { "totalLogs": 1250, "byAction": { "CREATE": 350, "UPDATE": 500, "DELETE": 150, "READ": 250 }, "byEntityType": { "customer": 400, "quotation": 300, "location": 200, "industrial_estate": 100, "master_option": 250 }, "todayCount": 45, "thisWeekCount": 320 } } ``` #### Get Logs by Entity ``` GET /api/audit-logs/entity/:entityType/:entityId ``` **Example:** ```bash curl http://localhost:3000/api/audit-logs/entity/customer/cust-001 ``` #### Get Logs by User ``` GET /api/audit-logs/user/:userId ``` **Example:** ```bash curl http://localhost:3000/api/audit-logs/user/user-123 ``` #### Get Single Audit Log ``` GET /api/audit-logs/:id ``` --- ## Available Data ### Customers - `branch-01`: 4 customers (3 active, 1 pending) - `branch-02`: 3 customers (1 active, 1 inactive, 1 pending) - `head-office`: 3 customers (all active) ### Quotations - `branch-01`: 2 quotations (1 sent, 1 accepted) - `branch-02`: 1 quotation (draft) - `head-office`: 1 quotation (sent) ### Master Options - Categories: `customer_type`, `payment_method`, `industry`, `lead_source` - Each category has multiple options with Thai/English names ### Locations - Countries: Thailand, etc. - Provinces: All Thai provinces - Districts/Subdistricts: Hierarchical data structure ### Industrial Estates - Multiple industrial estates across Thailand - Linked to locations with GPS coordinates ### Audit Logs - Complete audit trail for all operations - Admin-only access ## Testing with Browser Simply open these URLs in your browser: ### Customers - http://localhost:3000/api/customers/branch-01 - http://localhost:3000/api/customers/branch-02?status=active - http://localhost:3000/api/customers/head-office ### Quotations - http://localhost:3000/api/quotations/branch-01 - http://localhost:3000/api/quotations/head-office?status=sent ### Master Options - http://localhost:3000/api/master-options - http://localhost:3000/api/master-options?category=customer_type ### Locations - http://localhost:3000/api/locations - http://localhost:3000/api/locations/type/province - http://localhost:3000/api/locations?search=กรุงเทพ ### Industrial Estates - http://localhost:3000/api/industrial-estates - http://localhost:3000/api/industrial-estates?isActive=true ### Audit Logs (Admin only) - http://localhost:3000/api/audit-logs - http://localhost:3000/api/audit-logs/stats ## Project Structure This project follows the **Feature-based MVC pattern** as recommended by ElysiaJS: ``` src/ ├── app/ │ └── api/ │ └── [[...slugs]]/ │ └── route.ts # Main API entry point ├── modules/ │ ├── customers/ │ │ ├── controller.ts # HTTP handlers & routing │ │ ├── service.ts # Business logic │ │ └── model.ts # Schemas & validation │ ├── quotations/ │ │ ├── controller.ts # HTTP handlers & routing │ │ ├── service.ts # Business logic │ │ └── model.ts # Schemas & validation │ ├── master-options/ │ │ ├── controller.ts # HTTP handlers & routing │ │ ├── service.ts # Business logic │ │ └── model.ts # Schemas & validation │ ├── locations/ │ │ ├── controller.ts # HTTP handlers & routing │ │ ├── service.ts # Business logic │ │ └── model.ts # Schemas & validation │ ├── industrial-estates/ │ │ ├── controller.ts # HTTP handlers & routing │ │ ├── service.ts # Business logic │ │ └── model.ts # Schemas & validation │ └── audit-logs/ │ ├── controller.ts # HTTP handlers & routing │ ├── service.ts # Business logic │ └── model.ts # Schemas & validation ├── types/ │ └── customer.ts # Shared types ├── lib/ │ └── mock-data.ts # Mock data └── database/ └── schema.ts # Drizzle ORM schema ``` ### File Responsibilities #### Model (`model.ts`) - Define TypeBox schemas for validation - Export TypeScript types from schemas - All data structure definitions #### Service (`service.ts`) - Business logic and data operations - Pure functions (no Elysia dependencies) - CRUD operations - Data transformation #### Controller (`controller.ts`) - Elysia instance for the module - Route definitions and handlers - Request/response validation - Calls service functions - HTTP-specific concerns #### Main Route (`app/api/[[...slugs]]/route.ts`) - Import all controllers - Combine with `.use()` - Export handlers for Next.js ### Important Implementation Notes This project follows the **correct ElysiaJS + Next.js integration pattern**: - ✅ Single route file `[[...slugs]]/route.ts` with Elysia internal routing - ✅ Uses `export const GET = app.fetch` (not `.handle`) - ✅ Elysia instance has `prefix: '/api'` - ✅ All routes defined within Elysia instances using `.get()`, `.post()`, etc. - ✅ WinterCG compliant - works as normal Next.js API route - ✅ Feature-based MVC pattern for maintainability - ✅ Clear separation of concerns between Model, View, and Controller - ✅ Branch-level data scoping for multi-tenant architecture - ✅ Audit logging for all operations - ✅ Soft delete with `deletedAt` field - ✅ Multi-language support (Thai/English) ## Technologies Used - **ElysiaJS**: Type-safe, high-performance web framework - **Next.js 16**: React framework with App Router - **TypeScript**: Type safety throughout - **TypeBox**: Schema validation (via `@elysiajs/schema`) - **Drizzle ORM**: Type-safe SQL ORM - **PostgreSQL**: Primary database ## Features ✅ Feature-based MVC architecture ✅ Dynamic branch parameter support ✅ Type-safe request/response validation ✅ Optional query parameter filtering ✅ Mock data for customers and quotations ✅ Full TypeScript support ✅ Auto-generated API documentation (Swagger/OpenAPI ready) ✅ Correct ElysiaJS + Next.js integration pattern ✅ Scalable and maintainable code structure ✅ Clear separation of concerns ✅ Multi-tenant architecture with branch scoping ✅ Complete audit logging system ✅ Soft delete for data integrity ✅ Multi-language support (Thai/English) ✅ Hierarchical data structures (locations) ✅ GPS coordinate support (industrial estates) ✅ Admin-only access control (audit logs) ## Adding New Modules To add a new module (e.g., `products`): 1. Create folder: `src/modules/products/` 2. Create `model.ts` - Define schemas 3. Create `service.ts` - Business logic 4. Create `controller.ts` - Routes and handlers 5. Create `index.ts` - Module exports 6. Update `src/app/api/[[...slugs]]/route.ts`: ```typescript import { products } from "@/modules/products/controller"; const app = new Elysia({ prefix: "/api" }) .use(customers) .use(quotations) .use(masterOptions) .use(locations) .use(industrialEstates) .use(auditLogs) .use(products); // Add new module ``` ## Security & Access Control ### Branch Middleware All routes use `branchMiddleware` which injects: - `currentBranchId` - Current user's branch - `userId` - Current user ID - `userGroups` - User groups/roles - `accessibleBranches` - Branches user can access ### Permission Levels - **Standard Users**: Access to branch-scoped data - **Admin/Superadmin**: Full access + audit logs - **Auditor**: Read-only access to audit logs ### Data Isolation - All queries are automatically filtered by `branchId` - Cross-branch access is prevented - Soft delete ensures data integrity