Files
nextjs-elysia-allaos/API_DOCUMENTATION.md
phaichayon 043edff93a setup
2026-04-26 00:15:22 +07:00

18 KiB

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:
curl http://localhost:3000/api/customers/branch-01
  1. Get active customers from branch-02:
curl "http://localhost:3000/api/customers/branch-02?status=active"
  1. Get pending customers from head-office:
curl "http://localhost:3000/api/customers/head-office?status=pending"

Response Format:

{
  "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:
curl http://localhost:3000/api/quotations/branch-01
  1. Get sent quotations from head-office:
curl "http://localhost:3000/api/quotations/head-office?status=sent"

Response Format:

{
  "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:

curl "http://localhost:3000/api/master-options?category=customer_type&isActive=true"

Response Format:

{
  "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:

{
  "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:

curl "http://localhost:3000/api/locations?type=province&search=กรุงเทพ"

Response Format:

{
  "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:

curl http://localhost:3000/api/locations/type/province

Get Single Location

GET /api/locations/:id

Create Location

POST /api/locations

Request Body:

{
  "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:

curl "http://localhost:3000/api/industrial-estates?locationId=th-10&isActive=true"

Response Format:

{
  "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:

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:

{
  "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:

curl "http://localhost:3000/api/audit-logs?action=CREATE&limit=10"

Response Format:

{
  "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:

{
  "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:

curl http://localhost:3000/api/audit-logs/entity/customer/cust-001

Get Logs by User

GET /api/audit-logs/user/:userId

Example:

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

Quotations

Master Options

Locations

Industrial Estates

Audit Logs (Admin only)

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:

    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