Now.js Framework Documentation

Now.js Framework Documentation

ApiService - บริการ HTTP ระดับสูง

TH 31 Oct 2025 01:25

ApiService - บริการ HTTP ระดับสูง

เอกสารสำหรับ ApiService ซึ่งเป็นบริการ HTTP ระดับสูงของ Now.js Framework ที่รวมฟีเจอร์มากมายไว้ในจุดเดียว

📋 สารบัญ

  1. ภาพรวม
  2. การติดตั้งและนำเข้า
  3. การเริ่มต้นใช้งาน
  4. การกำหนดค่า
  5. เมธอด HTTP
  6. ระบบแคช
  7. การยืนยันตัวตน
  8. การป้องกันคำขอซ้ำซ้อน
  9. กลไกการลองใหม่
  10. การจัดการแคช
  11. ฟีเจอร์ขั้นสูง
  12. ตัวอย่างการใช้งาน
  13. เอกสารอ้างอิง API
  14. แนวทางปฏิบัติที่ดี

ภาพรวม

ApiService เป็น High-level HTTP Service ที่สร้างมาเพื่อจัดการ HTTP requests ในระดับที่ซับซ้อน มีฟีเจอร์ขั้นสูงที่ช่วยเพิ่มประสิทธิภาพและความปลอดภัย

ฟีเจอร์หลัก

  • การแคชในหน่วยความจำ: แคชข้อมูลใน memory พร้อมอายุการใช้งาน
  • การป้องกันคำขอซ้ำ: ป้องกันการเรียก API ซ้ำในเวลาเดียวกัน
  • กลไกการลองใหม่: ลองใหม่อัตโนมัติเมื่อเกิด network errors
  • การจัดการการยืนยันตัวตน: รองรับ Bearer, Basic, OAuth, Hybrid
  • การป้องกัน CSRF: ป้องกัน CSRF attacks อัตโนมัติ
  • การรีเฟรช JWT อัตโนมัติ: Refresh token ก่อนหมดอายุ
  • การติดตามคำขอ: ติดตามประสิทธิภาพและข้อผิดพลาด
  • การยกเลิกคำขอ: ยกเลิก requests ที่กำลังทำงาน
  • การดึงข้อมูลแบบช่วงเวลา: Poll endpoints อัตโนมัติ
  • คำขอตามลำดับ: ทำ requests ตามลำดับที่กำหนด

เมื่อไหร่ควรใช้ ApiService

ใช้ ApiService เมื่อ:

  • ต้องการ caching สำหรับข้อมูลที่ไม่เปลี่ยนแปลงบ่อย
  • ต้องการ retry logic สำหรับ network errors
  • ต้องการ authentication management
  • ต้องการป้องกันการเรียก API ซ้ำ
  • ต้องการ request tracking และ analytics

ไม่ควรใช้ ApiService เมื่อ:

  • ต้องการความเรียบง่าย (ใช้ simpleFetch หรือ http แทน)
  • ทำงานกับ real-time data (ไม่ควร cache)
  • ไม่ต้องการฟีเจอร์ขั้นสูง

การติดตั้งและนำเข้า

ApiService โหลดมาพร้อมกับ Now.js Framework และพร้อมใช้งานทันทีผ่าน window object:

// ไม่ต้อง import - พร้อมใช้งานทันที
console.log(window.ApiService); // อ็อบเจ็กต์ ApiService บน window

การเริ่มต้นใช้งาน

การเริ่มต้นแบบพื้นฐาน

// เริ่มต้นด้วยการตั้งค่าเริ่มต้น
await ApiService.init();

// ตอนนี้คุณสามารถเรียก request ได้
const response = await ApiService.get('/api/users');
console.log(response.data);

การเริ่มต้นพร้อมการกำหนดค่า

await ApiService.init({
  baseURL: 'https://api.example.com',
  debug: true,

  cache: {
    enabled: true,
    expiry: {
      get: 300000, // แคชคำขอ GET เป็นเวลา 5 นาที
      post: 0,     // ไม่แคช POST
      put: 0,
      delete: 0
    }
  },

  security: {
    csrfProtection: true,
    bearerAuth: true,
    bearerTokenKey: 'auth_token'
  },

  connection: {
    timeout: 30000,
    retryOnNetworkError: true,
    maxNetworkRetries: 3
  }
});

การกำหนดค่า

ApiService มี configuration หลายส่วนที่สามารถกำหนดได้

1. การตั้งค่าทั่วไป

{
  baseURL: '',              // Base URL สำหรับทุกคำขอ
  debug: false,             // เปิดใช้งาน debug logging
  retryCount: 3,            // จำนวนครั้งในการลองใหม่
  retryDelay: 1000,         // ระยะเวลาหน่วงระหว่างการลองใหม่ (มิลลิวินาที)
  deduplicate: true         // เปิดใช้งานการป้องกันคำขอซ้ำซ้อน
}

2. การตั้งค่าความปลอดภัย

{
  security: {
    // การป้องกัน CSRF
    csrfProtection: true,
    csrfHeaderName: 'X-CSRF-Token',
    csrfCookieName: 'XSRF-TOKEN',
    csrfTokenSelector: 'meta[name="csrf-token"]',
    csrfIncludeSafeMethods: true,

    // การยืนยันตัวตนด้วย Bearer Token
    bearerAuth: false,
    bearerTokenKey: 'auth_token',
    bearerPrefix: 'Bearer ',

    // การยืนยันตัวตนแบบ Basic
    basicAuth: false,
    basicUsername: '',
    basicPassword: '',

  // OAuth (โฟลว์การยืนยันตัวตนแบบ OAuth)
  oauth: false,
  oauthTokenKey: 'oauth_token',

    // การรีเฟรช JWT อัตโนมัติ
    jwtRefresh: false,
    jwtRefreshEndpoint: 'api/auth/refresh',
    jwtExpireKey: 'exp',
    jwtRefreshBeforeExpirySec: 300,

  // กลยุทธ์การยืนยันตัวตน
  authStrategy: 'hybrid',  // 'hybrid', 'cookie', 'storage' (โหมดผสม, คุกกี้, ที่เก็บข้อมูล)
    sendCredentials: true    // ส่ง cookies พร้อมกับ requests
  }
}

3. การตั้งค่าการเชื่อมต่อ

{
  connection: {
    timeout: 30000,                           // เวลารอคำขอ (มิลลิวินาที)
    retryOnNetworkError: true,                // ลองใหม่เมื่อเกิด network errors
    maxNetworkRetries: 3,                     // จำนวนครั้งสูงสุดในการลองใหม่
    backoffFactor: 1.5,                       // ตัวคูณ backoff
    retryStatusCodes: [408, 429, 500, 502, 503, 504],
    exponentialBackoff: true                  // ใช้ exponential backoff
  }
}

4. การตั้งค่าแคช

{
  cache: {
    enabled: true,
    storageType: 'memory',     // 'memory', 'session', 'local'
    maxSize: 100,              // จำนวนรายการแคชสูงสุด
    expiry: {
      default: 60000,          // 1 นาที
      get: 60000,              // แคช GET นาน 1 นาที
      post: 0,                 // ไม่แคช POST
      put: 0,                  // ไม่แคช PUT
      delete: 0                // ไม่แคช DELETE
    },
    keyGeneratorFn: null,      // ฟังก์ชันสร้าง cache key แบบกำหนดเอง
    responsePredicate: null    // ฟังก์ชันตัดสินใจว่าจะแคชหรือไม่
  }
}

5. การตั้งค่าการติดตาม

{
  tracking: {
    enabled: false,
    errorTracking: true,
    performanceTracking: false,
    analyticsCallback: null,   // ฟังก์ชันรับข้อมูล analytics
    excludePaths: []           // เส้นทางที่ไม่ต้องการติดตาม
  }
}

6. การตั้งค่าการบันทึกล็อก

{
  logging: {
    enabled: false,
    logLevel: 'error',         // 'debug', 'info', 'warn', 'error'
    includeRequest: true,
    includeResponse: true,
    logToConsole: true,
    customLogger: null         // ฟังก์ชันบันทึกล็อกแบบกำหนดเอง
  }
}

เมธอด HTTP

ApiService มี HTTP methods หลักๆ ที่ใช้งานได้

คำขอ GET

// GET แบบง่าย
const response = await ApiService.get('/api/users');
console.log(response.data);

// GET พร้อมพารามิเตอร์
const response = await ApiService.get('/api/users', {
  page: 1,
  limit: 10,
  sort: 'name'
});

// GET พร้อมตัวเลือก
const response = await ApiService.get('/api/users', {}, {
  cache: { enabled: false },  // ปิดการแคชสำหรับคำขอนี้
  timeout: 5000               // กำหนดเวลารอแบบกำหนดเอง
});

คำขอ POST

// POST แบบง่าย
const response = await ApiService.post('/api/users', {
  name: 'John Doe',
  email: 'john@example.com'
});

// POST พร้อมตัวเลือก
const response = await ApiService.post('/api/users', userData, {
  headers: {
    'X-Custom-Header': 'value'
  }
});

คำขอ PUT

// อัปเดตข้อมูล
const response = await ApiService.put('/api/users/1', {
  name: 'Jane Doe',
  email: 'jane@example.com'
});

คำขอ DELETE

// ลบข้อมูล
const response = await ApiService.delete('/api/users/1');

// DELETE พร้อมตัวเลือก
const response = await ApiService.delete('/api/users/1', {
  headers: {
    'X-Reason': 'Account closed'
  }
});

วัตถุ Response

ทุก method คืนค่า response object ที่มีโครงสร้างเหมือนกับ HttpClient:

{
  ok: boolean,              // true ถ้าสถานะ 200-299
  status: number,           // รหัสสถานะ HTTP
  statusText: string,       // ข้อความสถานะ HTTP
  data: any,                // ข้อมูล response ที่แปลงแล้ว
  headers: object,          // เฮดเดอร์ของ response
  url: string,              // URL ของคำขอ
  fromCache: boolean,       // true ถ้ามาจาก cache (GET เท่านั้น)
  timestamp: number,        // เวลาสแตมป์ (ถ้ามี)
  requestId: string|null    // ID ของคำขอ (ถ้ามี)
}

ระบบแคช

ApiService มีระบบ caching ที่ทรงพลังและยืดหยุ่น

การแคชอัตโนมัติ

await ApiService.init({
  cache: {
    enabled: true,
    expiry: {
      get: 300000  // แคชคำขอ GET นาน 5 นาที
    }
  }
});

// การเรียกครั้งแรก - จะเรียก API
const response1 = await ApiService.get('/api/categories');
console.log(response1.fromCache); // false (โหลดจาก API จริง)

// การเรียกครั้งที่สองภายใน 5 นาที - จะได้จาก cache
const response2 = await ApiService.get('/api/categories');
console.log(response2.fromCache); // true (ดึงจากแคช)

แคชตามประเภทที่เก็บข้อมูล

Memory Cache (ค่าเริ่มต้น)

await ApiService.init({
  cache: {
    enabled: true,
    storageType: 'memory'  // ค่าเริ่มต้น - เก็บใน memory
  }
});

ข้อดี:

  • ✅ เร็วที่สุด
  • ✅ ไม่มีขนาดจำกัด (ขึ้นกับ memory)

ข้อเสีย:

  • ❌ หายเมื่อ refresh หน้า

Session Storage Cache

await ApiService.init({
  cache: {
    enabled: true,
    storageType: 'session'  // เก็บใน sessionStorage
  }
});

ข้อดี:

  • ✅ คงอยู่ตลอด session
  • ✅ เร็ว

ข้อเสีย:

  • ❌ หายเมื่อปิด tab
  • ❌ จำกัดขนาด (~5-10MB)

Local Storage Cache

await ApiService.init({
  cache: {
    enabled: true,
    storageType: 'local'    // เก็บใน localStorage
  }
});

ข้อดี:

  • ✅ คงอยู่แม้ปิดเบราว์เซอร์
  • ✅ แชร์ระหว่าง tabs

ข้อเสีย:

  • ❌ จำกัดขนาด (~5-10MB)
  • ❌ ช้ากว่า memory

Cache Key แบบกำหนดเอง

await ApiService.init({
  cache: {
    enabled: true,
    keyGeneratorFn: (url, params, method) => {
      // ตรรกะ cache key แบบกำหนดเอง
      const userId = localStorage.getItem('user_id');
      return `${userId}:${method}:${url}:${JSON.stringify(params)}`;
    }
  }
});

การแคชแบบมีเงื่อนไข

await ApiService.init({
  cache: {
    enabled: true,
    responsePredicate: (response) => {
      // แคชเฉพาะ response ที่สำเร็จ
      return response.ok && response.status === 200;
    }
  }
});

ระยะเวลาแคชตามเมธอด

await ApiService.init({
  cache: {
    enabled: true,
    expiry: {
      get: 600000,    // แคช GET นาน 10 นาที
      post: 0,        // ไม่แคช POST
      put: 0,         // ไม่แคช PUT
      delete: 0       // ไม่แคช DELETE
    }
  }
});

การยืนยันตัวตน

ApiService รองรับ authentication หลายแบบ

1. การยืนยันตัวตนด้วย Bearer Token

กลยุทธ์ Storage (เก่า)

await ApiService.init({
  security: {
    bearerAuth: true,
    bearerTokenKey: 'auth_token',
    authStrategy: 'storage'  // ใช้ localStorage
  }
});

// ตั้งค่า token ใน localStorage
localStorage.setItem('auth_token', 'your-token-here');

// Requests จะรวม: Authorization: Bearer your-token อัตโนมัติ
const response = await ApiService.get('/api/user/profile');

กลยุทธ์ Hybrid (แนะนำ)

await ApiService.init({
  security: {
    bearerAuth: true,
    authStrategy: 'hybrid'  // HttpOnly cookie + in-memory token (โหมดผสม)
  }
});

// ตั้งค่า access token (จาก login response)
ApiService.setAccessToken('short-lived-access-token');

// Requests จะรวม: Authorization: Bearer short-lived-access-token
const response = await ApiService.get('/api/user/profile');

// ลบ token เมื่อ logout
ApiService.clearAccessToken();
await ApiService.init({
  security: {
    authStrategy: 'cookie',  // เซิร์ฟเวอร์จัดการ auth ผ่าน cookie
    sendCredentials: true
  }
});

// ไม่มี Authorization header - เซิร์ฟเวอร์ใช้ HttpOnly cookie
const response = await ApiService.get('/api/user/profile');

2. การยืนยันตัวตนแบบ Basic

await ApiService.init({
  security: {
    basicAuth: true,
    basicUsername: 'admin',
    basicPassword: 'secret123'
  }
});

// Requests จะรวม: Authorization: Basic YWRtaW46c2VjcmV0MTIz
const response = await ApiService.get('/api/admin/stats');

3. การรีเฟรช JWT อัตโนมัติ

await ApiService.init({
  security: {
    bearerAuth: true,
    authStrategy: 'hybrid',
    jwtRefresh: true,
    jwtRefreshEndpoint: '/api/auth/refresh',
    jwtExpireKey: 'exp',
    jwtRefreshBeforeExpirySec: 300  // รีเฟรช 5 นาทีก่อนหมดอายุ
  }
});

// ApiService จะทำอัตโนมัติ:
// 1. แปลง JWT token เพื่อตรวจสอบการหมดอายุ
// 2. รีเฟรช token 5 นาทีก่อนหมดอายุ
// 3. อัปเดต access token ใน memory

// คุณไม่ต้องทำอะไร - ใช้ ApiService ตามปกติ
const response = await ApiService.get('/api/user/profile');

4. การป้องกัน CSRF

await ApiService.init({
  security: {
    csrfProtection: true,
    csrfHeaderName: 'X-CSRF-Token',
    csrfCookieName: 'XSRF-TOKEN',
    csrfTokenSelector: 'meta[name="csrf-token"]'
  }
});

// CSRF token จะถูก:
// 1. ดึงมาจาก meta tag หรือ cookie อัตโนมัติ
// 2. แนบเข้ากับ POST/PUT/DELETE requests

HTML:

<meta name="csrf-token" content="your-csrf-token">

การป้องกันคำขอซ้ำซ้อน

ApiService ป้องกันการเรียก API ซ้ำในเวลาเดียวกัน

วิธีการทำงาน

await ApiService.init({
  deduplicate: true  // ค่าเริ่มต้น
});

// ผู้ใช้คลิกปุ่มหลายครั้งอย่างรวดเร็ว
button.addEventListener('click', async () => {
  const response = await ApiService.get('/api/data');
  console.log(response.data);
});

// ApiService จะ:
// 1. ตรวจสอบว่า request กำลัง pending อยู่หรือไม่
// 2. ถ้าใช่ คืนค่า promise เดียวกัน
// 3. ถ้าไม่ใช่ ทำ request ใหม่
// ผลลัพธ์: มีเพียง 1 การเรียก API จริง ทุกการคลิกใช้ response เดียวกัน

ปิดการป้องกันคำขอซ้ำ

await ApiService.init({
  deduplicate: false  // อนุญาตให้มี duplicate requests
});

Cache Key แบบกำหนดเอง

การป้องกันคำขอซ้ำใช้ cache key เพื่อระบุ requests:

// cache key เหมือนกัน = ถูกป้องกันคำขอซ้ำ
await ApiService.get('/api/users', { page: 1 });
await ApiService.get('/api/users', { page: 1 });  // ใช้ request แรก

// cache key ต่างกัน = requests แยก
await ApiService.get('/api/users', { page: 1 });
await ApiService.get('/api/users', { page: 2 });  // ทำ request ที่สอง

กลไกการลองใหม่

ApiService ลองใหม่อัตโนมัติเมื่อเกิดข้อผิดพลาด

การลองใหม่พื้นฐาน

await ApiService.init({
  retryCount: 3,
  retryDelay: 1000,
  connection: {
    retryOnNetworkError: true,
    maxNetworkRetries: 3
  }
});

// การลองใหม่จะเกิดขึ้นเมื่อ:
// 1. เครือข่ายขัดข้อง
// 2. หมดเวลา
// 3. ข้อผิดพลาดของเซิร์ฟเวอร์ (500, 502, 503, 504)

การถอยกลับแบบเอ็กซ์โปเนนเชียล

await ApiService.init({
  retryDelay: 1000,
  connection: {
    retryOnNetworkError: true,
    maxNetworkRetries: 3,
    backoffFactor: 1.5,
    exponentialBackoff: true
  }
});

// ลองใหม่อีกครั้ง:
// ครั้งที่ 1: 1000ms
// ครั้งที่ 2: 1500ms (1000 * 1.5)
// ครั้งที่ 3: 2250ms (1500 * 1.5)

สถานะที่ลองใหม่ได้

await ApiService.init({
  connection: {
    retryStatusCodes: [408, 429, 500, 502, 503, 504],
    maxNetworkRetries: 3
  }
});

// จะลองใหม่สำหรับรหัสสถานะต่อไปนี้:
// 408 - หมดเวลา
// 429 - ติดต่อมากเกินไป
// 500 - ข้อผิดพลาดของเซิร์ฟเวอร์
// 502 - เกตเวย์ไม่ถูกต้อง
// 503 - ไม่พร้อมให้บริการ
// 504 - หมดเวลาเกตเวย์

การจัดการแคช

ApiService มี methods สำหรับจัดการ cache

ล้างแคชทั้งหมด

// Clear all cache entries
ApiService.clearCache();

การลบแคชแบบกำหนดเอง

// Invalidate by URL and params
ApiService.invalidateCache('/api/users', { page: 1 });

// Invalidate by URL (all params)
ApiService.invalidateCacheByUrl('/api/users');

การลบแคชอัตโนมัติ

// POST/PUT/DELETE จะลบแคชที่เกี่ยวข้องอัตโนมัติ
await ApiService.post('/api/users', userData);
// ^ จะลบแคชสำหรับ '/api/users'

await ApiService.put('/api/users/1', userData);
// ^ จะลบแคชสำหรับ '/api/users/1'

await ApiService.delete('/api/users/1');
// ^ จะลบแคชสำหรับ '/api/users/1'

ตรวจสอบสถานะแคช

const response = await ApiService.get('/api/users');

if (response.fromCache) {
  console.log('ข้อมูลจากแคช');
} else {
  console.log('ข้อมูลจาก API');
}

ฟีเจอร์ขั้นสูง

1. ยกเลิกคำขอ

// เริ่ม request
const requestPromise = ApiService.get('/api/large-data');

// ยกเลิกหลังจาก 5 วินาที
setTimeout(() => {
  ApiService.abort('/api/large-data');
}, 5000);

try {
  const response = await requestPromise;
} catch (error) {
  console.log('Request ถูกยกเลิก');
}

2. การดึงข้อมูลเป็นช่วง

// Poll ทุก 5 วินาที
const stopPolling = ApiService.poll(
  '/api/status',
  {},
  5000,
  (response, error, count) => {
    if (error) {
      console.error('Poll error:', error);
      return;
    }

    console.log('Poll #' + count, response.data);
  },
  {
    maxPolls: 10,  // หยุดหลัง 10 polls
    condition: (response) => {
      // หยุดเมื่อสถานะเป็น 'completed'
      return response.data.status === 'completed';
    }
  }
);

// หยุด polling ด้วยตนเอง
// stopPolling();

3. คำขอตามลำดับ

const requests = [
  { url: '/api/users', params: { page: 1 } },
  { url: '/api/posts', params: { page: 1 } },
  { url: '/api/comments', params: { page: 1 } }
];

const results = await ApiService.sequence(requests, (response) => {
  // ประมวลผล response แต่ละตัว
  return response.data;
});

console.log('ผลลัพธ์ทั้งหมด:', results);

4. คำขอแบบขนาน

const responses = await ApiService.all([
  ApiService.get('/api/users'),
  ApiService.get('/api/posts'),
  ApiService.get('/api/comments')
]);

console.log('Users:', responses[0].data);
console.log('Posts:', responses[1].data);
console.log('Comments:', responses[2].data);

5. การบันทึกล็อกแบบกำหนดเอง

await ApiService.init({
  logging: {
    enabled: true,
    logLevel: 'debug',
    customLogger: (level, message, data) => {
      // ส่งไปยังบริการบันทึกล็อกภายนอก
      console.log(`[${level.toUpperCase()}] ${message}`, data);

      // ตัวอย่าง: ส่งไป Sentry
      if (level === 'error') {
        // Sentry.captureException(data);
      }
    }
  }
});

6. การติดตามประสิทธิภาพ

await ApiService.init({
  tracking: {
    enabled: true,
    performanceTracking: true,
    analyticsCallback: (data) => {
      if (data.type === 'performance') {
        console.log(`${data.method} ${data.url} ใช้เวลา ${data.duration}ms`);

        // ส่งไปยังบริการ analytics
        // analytics.track('api_request', data);
      }
    }
  }
});

ตัวอย่างการใช้งาน

1. การเรียก API แบบง่ายพร้อมแคช

await ApiService.init({
  baseURL: 'https://api.example.com',
  cache: {
    enabled: true,
    expiry: { get: 300000 }  // 5 นาที
  }
});

// การเรียกครั้งแรก
const categories = await ApiService.get('/api/categories');
console.log(categories.fromCache); // false

// การเรียกครั้งที่สอง (ภายใน 5 นาที)
const categoriesCached = await ApiService.get('/api/categories');
console.log(categoriesCached.fromCache); // true

2. การยืนยันตัวตนด้วย Bearer Token

await ApiService.init({
  baseURL: 'https://api.example.com',
  security: {
    bearerAuth: true,
    authStrategy: 'hybrid'
  }
});

// เข้าสู่ระบบ
const loginResponse = await ApiService.post('/api/auth/login', {
  email: 'user@example.com',
  password: 'password123'
});

// ตั้งค่า access token
ApiService.setAccessToken(loginResponse.data.accessToken);

// ทำ requests ที่ผ่านการยืนยันตัวตน
const profile = await ApiService.get('/api/user/profile');
console.log(profile.data);

// ออกจากระบบ
await ApiService.post('/api/auth/logout');
ApiService.clearAccessToken();

3. การส่งฟอร์มพร้อม CSRF

await ApiService.init({
  security: {
    csrfProtection: true
  }
});

// CSRF token จะถูกแนบอัตโนมัติ
const response = await ApiService.post('/api/contact', {
  name: 'John Doe',
  email: 'john@example.com',
  message: 'Hello!'
});

4. การลองใหม่เมื่อเกิด Network Error

await ApiService.init({
  connection: {
    retryOnNetworkError: true,
    maxNetworkRetries: 3,
    backoffFactor: 1.5,
    exponentialBackoff: true
  }
});

// จะลองใหม่ 3 ครั้งเมื่อเกิด network error
try {
  const response = await ApiService.get('/api/data');
  console.log(response.data);
} catch (error) {
  console.error('ล้มเหลวหลังจากลองใหม่ 3 ครั้ง:', error);
}

5. การ Poll สำหรับอัปเดตสถานะ

await ApiService.init({
  baseURL: 'https://api.example.com'
});

// เริ่มงานที่ใช้เวลานาน
const task = await ApiService.post('/api/tasks', {
  type: 'export',
  format: 'pdf'
});

// Poll สถานะ
const stopPolling = ApiService.poll(
  `/api/tasks/${task.data.id}`,
  {},
  2000,  // Poll ทุก 2 วินาที
  (response, error, count) => {
    if (error) {
      console.error('Poll error:', error);
      return;
    }

    const status = response.data.status;
    console.log(`Poll #${count}: ${status}`);

    if (status === 'completed') {
      console.log('งานเสร็จสมบูรณ์!', response.data.result);
    }
  },
  {
    maxPolls: 30,  // สูงสุด 1 นาที (30 * 2 วินาที)
    condition: (response) => {
      // หยุดเมื่อเสร็จสมบูรณ์หรือล้มเหลว
      return ['completed', 'failed'].includes(response.data.status);
    }
  }
);

6. การแบ่งหน้าพร้อมแคช

await ApiService.init({
  baseURL: 'https://api.example.com',
  cache: {
    enabled: true,
    expiry: { get: 60000 }  // 1 นาที
  }
});

async function loadPage(page) {
  const response = await ApiService.get('/api/products', {
    page: page,
    limit: 20
  });

  console.log(`หน้า ${page} (cached: ${response.fromCache})`);
  return response.data;
}

// โหลดหน้า 1-3
const page1 = await loadPage(1);  // เรียก API
const page2 = await loadPage(2);  // เรียก API
const page3 = await loadPage(3);  // เรียก API

// โหลดหน้า 1 อีกครั้ง (ภายใน 1 นาที)
const page1Again = await loadPage(1);  // จาก cache

7. การอัปโหลดไฟล์

const fileInput = document.querySelector('#file');
const file = fileInput.files[0];

const formData = new FormData();
formData.append('file', file);
formData.append('title', 'My Document');
formData.append('category', 'reports');

const response = await ApiService.post('/api/upload', formData);

if (response.ok) {
  console.log('อัปโหลดสำเร็จ:', response.data.url);
}

8. การค้นหาพร้อม Debouncing

let searchTimeout;

function handleSearch(query) {
  // ล้าง timeout ก่อนหน้า
  clearTimeout(searchTimeout);

  // ยกเลิก request ก่อนหน้า
  ApiService.abort('/api/search', { q: lastQuery });

  // ตั้ง timeout ใหม่
  searchTimeout = setTimeout(async () => {
    const response = await ApiService.get('/api/search', {
      q: query,
      limit: 10
    });

    displayResults(response.data);
  }, 300);  // รอ 300ms หลังจากผู้ใช้หยุดพิมพ์
}

searchInput.addEventListener('input', (e) => {
  handleSearch(e.target.value);
});

9. การแคชแบบมีเงื่อนไข

await ApiService.init({
  cache: {
    enabled: true,
    expiry: { get: 300000 },
    responsePredicate: (response) => {
      // แคชเฉพาะ response ที่สำเร็จและมีข้อมูล
      return response.ok &&
             response.status === 200 &&
             response.data &&
             Object.keys(response.data).length > 0;
    }
  }
});

const response = await ApiService.get('/api/data');
// จะถูกแคชเฉพาะเมื่อ response สำเร็จและมีข้อมูล

10. การจัดการข้อผิดพลาด

try {
  const response = await ApiService.get('/api/users');

  if (response.ok) {
    console.log('สำเร็จ:', response.data);
  } else {
    // จัดการ HTTP errors
    switch (response.status) {
      case 401:
        // เปลี่ยนไปหน้า login
        window.location.href = '/login';
        break;

      case 403:
        alert('ไม่มีสิทธิ์เข้าถึง');
        break;

      case 404:
        console.error('ไม่พบข้อมูล');
        break;

      case 500:
        alert('เกิดข้อผิดพลาดจากเซิร์ฟเวอร์ กรุณาลองใหม่อีกครั้ง');
        break;

      default:
        console.error('ข้อผิดพลาด:', response.statusText);
    }
  }
} catch (error) {
  // จัดการ network errors
  console.error('ข้อผิดพลาดเครือข่าย:', error);
  alert('ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ กรุณาตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณ');
}

เอกสารอ้างอิง API

การเริ่มต้น

ApiService.init(options?: ConfigOptions): Promise<ApiService>

เมธอด HTTP

get(url: string, params?: object, options?: RequestOptions): Promise<Response>
post(url: string, data: any, options?: RequestOptions): Promise<Response>
put(url: string, data: any, options?: RequestOptions): Promise<Response>
delete(url: string, options?: RequestOptions): Promise<Response>

การยืนยันตัวตน

setAccessToken(token: string): void
clearAccessToken(): void

การจัดการแคช

clearCache(): void
invalidateCache(url: string, params?: object): boolean
invalidateCacheByUrl(url: string): number
createCacheKey(url: string, params?: object, method?: string): string

การควบคุมคำขอ

abort(url: string, params?: object): boolean

เมธอดขั้นสูง

all(requests: Promise[]): Promise<any[]>
poll(url: string, params?: object, interval?: number, callback: Function, options?: PollOptions): Function
sequence(requests: RequestConfig[], processor?: Function): Promise<any[]>

เมธอดยูทิลิตี้

buildUrlWithParams(url: string, params?: object): string

แนวทางปฏิบัติที่ดี

1. ✅ เริ่มต้นครั้งเดียว

// ❌ ไม่ดี: Initialize ทุกครั้ง
async function fetchData() {
  await ApiService.init({ /* config */ });
  return await ApiService.get('/api/data');
}

// ✅ ดี: Initialize ครั้งเดียว
await ApiService.init({ /* config */ });

async function fetchData() {
  return await ApiService.get('/api/data');
}

2. ✅ ใช้การหมดอายุแคชที่เหมาะสม

// ❌ ไม่ดี: Cache ทุกอย่างเหมือนกัน
await ApiService.init({
  cache: {
    enabled: true,
    expiry: { get: 300000 }  // 5 นาทีสำหรับทุกอย่าง
  }
});

// ✅ ดี: Cache ตามประเภทข้อมูล
await ApiService.init({
  cache: {
    enabled: true,
    expiry: {
      get: 300000,  // 5 นาทีสำหรับข้อมูลปกติ
      post: 0,      // ไม่แคช POST
      put: 0,
      delete: 0
    }
  }
});

// ✅ ดีกว่า: ปิดแคชสำหรับแต่ละคำขอ
const realtimeData = await ApiService.get('/api/live-stats', {}, {
  cache: { enabled: false }
});

3. ✅ ใช้ Hybrid Auth Strategy

// ❌ ไม่ดี: Storage strategy (ไม่ปลอดภัย)
await ApiService.init({
  security: {
    bearerAuth: true,
    authStrategy: 'storage'  // Token อยู่ใน localStorage
  }
});

// ✅ ดี: Hybrid strategy (ปลอดภัยกว่า)
await ApiService.init({
  security: {
    bearerAuth: true,
    authStrategy: 'hybrid',  // Token in memory + HttpOnly cookie
    jwtRefresh: true
  }
});

4. ✅ จัดการข้อผิดพลาดอย่างเหมาะสม

// ❌ ไม่ดี: ไม่จัดการ error
const response = await ApiService.get('/api/data');
console.log(response.data);  // May be undefined

// ✅ ดี: ตรวจสอบ response.ok
const response = await ApiService.get('/api/data');
if (response.ok) {
  console.log(response.data);
} else {
  console.error('Error:', response.statusText);
}

// ✅ ดีกว่า: ใช้ try-catch
try {
  const response = await ApiService.get('/api/data');
  if (response.ok) {
    console.log(response.data);
  }
} catch (error) {
  console.error('Network error:', error);
}

5. ✅ เปิดใช้งาน CSRF Protection

// ✅ ดี: เปิดใช้งาน CSRF protection
await ApiService.init({
  security: {
    csrfProtection: true
  }
});

6. ✅ ใช้กลไก Retry

// ✅ ดี: เปิดใช้งาน retry
await ApiService.init({
  connection: {
    retryOnNetworkError: true,
    maxNetworkRetries: 3,
    exponentialBackoff: true
  }
});

7. ✅ ทำความสะอาด Polling เมื่อไม่ต้องการแล้ว

// ❌ ไม่ดี: ไม่ cleanup
ApiService.poll('/api/status', {}, 5000, callback);

// ✅ ดี: เก็บ stop function
const stopPolling = ApiService.poll('/api/status', {}, 5000, callback);

// Cleanup เมื่อไม่ต้องการแล้ว
window.addEventListener('beforeunload', () => {
  stopPolling();
});

8. ✅ ยกเลิกคำขอที่ไม่ต้องการ

// ✅ ดี: Abort requests ที่ไม่ต้องการแล้ว
const controller = new AbortController();

button.addEventListener('click', () => {
  ApiService.get('/api/large-data', {}, {
    signal: controller.signal
  });
});

cancelButton.addEventListener('click', () => {
  ApiService.abort('/api/large-data');
});

9. ✅ ใช้การป้องกันการเรียกซ้ำ

// ✅ ดี: เปิดใช้งาน deduplication (default)
await ApiService.init({
  deduplicate: true
});

// ป้องกันการคลิกซ้ำๆ
button.addEventListener('click', async () => {
  const response = await ApiService.get('/api/data');
  // ถ้าคลิกหลายครั้ง จะใช้ request เดียวกัน
});

10. ✅ ลบแคชเมื่อข้อมูลเปลี่ยนแปลง

// ❌ ไม่ดี: ไม่ clear cache เลย
await ApiService.post('/api/users', userData);
// Cache เก่ายังคงอยู่

// ✅ ดี: ApiService clear cache อัตโนมัติ
await ApiService.post('/api/users', userData);
// Cache สำหรับ '/api/users' ถูกลบอัตโนมัติ

// ✅ ดีกว่า: Clear cache manually เมื่อจำเป็น
await ApiService.post('/api/settings', settings);
ApiService.invalidateCacheByUrl('/api');  // Clear all /api cache

สรุป

เมื่อไหร่ควรใช้ ApiService

กรณีการใช้งาน ใช้ ApiService?
ข้อมูลที่ไม่เปลี่ยนแปลงบ่อย (categories, config) ✅ ใช่ - เปิด caching
ข้อมูล real-time (stock prices, live stats) ❌ ไม่ - ใช้ http หรือ disable cache
ต้องการ authentication ✅ ใช่ - มี auth management
ต้องการ retry logic ✅ ใช่ - มี retry mechanism
ป้องกันการคลิกซ้ำ ✅ ใช่ - มี deduplication
Simple API call ❌ ไม่จำเป็น - ใช้ simpleFetch หรือ http
File download/upload ✅ ใช้ได้ - รองรับ FormData
Polling ✅ ใช่ - มี built-in polling
Performance tracking ✅ ใช่ - มี tracking system

ฟีเจอร์หลัก

คุณสมบัติ พร้อมใช้งาน หมายเหตุ
Caching Memory, Session, Local storage
Deduplication ป้องกันคำขอซ้ำซ้อน
Retry พร้อม exponential backoff
Authentication Bearer, Basic, OAuth, Hybrid
CSRF Protection อัตโนมัติ
JWT Refresh รีเฟรชอัตโนมัติก่อนหมดอายุ
Polling พร้อมเงื่อนไข
Abort ยกเลิกคำขอที่กำลังรอ
Tracking ประสิทธิภาพและข้อผิดพลาด
Logging หลายระดับ