ข้ามไปยังเนื้อหาหลัก

การจัดการ API Requests

Shared API Requests

เริ่มด้วยการวาง Logic การเรียก API ที่ใช้ร่วมกันไว้ใน shared/api สิ่งนี้ช่วยให้คุณ Reuse คำสั่ง Request ได้ง่ายทั่วทั้งแอป และช่วยให้ Prototyping ได้เร็วขึ้น สำหรับหลายๆ โปรเจกต์ แค่นี้ก็เพียงพอแล้วสำหรับการเรียก API

โครงสร้างไฟล์ทั่วไปจะเป็นดังนี้:

  • 📂 shared
    • 📂 api
      • 📄 client.ts
      • 📄 index.ts
      • 📂 endpoints
        • 📄 login.ts

ไฟล์ client.ts รวมการตั้งค่า HTTP request ไว้ที่เดียว มันจะห่อหุ้ม Method ที่คุณเลือกใช้ (เช่น fetch() หรือ axios instance) และจัดการ Configuration ทั่วไป เช่น:

  • Backend base URL
  • Default headers (เช่น สำหรับ authentication)
  • Data serialization

นี่คือตัวอย่างสำหรับ axios และ fetch:

shared/api/client.ts
// Example using axios
import axios from 'axios';

export const client = axios.create({
baseURL: 'https://your-api-domain.com/api/',
timeout: 5000,
headers: { 'X-Custom-Header': 'my-custom-value' }
});

จัดระเบียบฟังก์ชัน API request แต่ละตัวของคุณใน shared/api/endpoints โดยจัดกลุ่มตาม API endpoint

note

เพื่อให้ตัวอย่างกระชับ เราจะละเว้นเรื่องการโต้ตอบกับ Form และ Validation สำหรับรายละเอียดเกี่ยวกับ Libraries เช่น Zod หรือ Valibot ให้ดูที่บทความ Type Validation and Schemas

shared/api/endpoints/login.ts
import { client } from '../client';

export interface LoginCredentials {
email: string;
password: string;
}

export function login(credentials: LoginCredentials) {
return client.post('/login', credentials);
}

ใช้ไฟล์ index.ts ใน shared/api เพื่อ Export request functions ของคุณออกไป

shared/api/index.ts
export { client } from './client'; // ถ้าคุณต้องการ Export client เองด้วย
export { login } from './endpoints/login';
export type { LoginCredentials } from './endpoints/login';

Slice-Specific API Requests

ถ้า API request ถูกใช้โดย Slice ใด Slice หนึ่งเท่านั้น (เช่น หน้าเดียว หรือ Feature เดียว) และจะไม่ถูก Reuse ที่อื่น ให้วางมันไว้ใน api segment ของ Slice นั้นๆ วิธีนี้จะช่วยเก็บ Logic เฉพาะของ Slice ไว้ด้วยกันอย่างเป็นระเบียบ

  • 📂 pages
    • 📂 login
      • 📄 index.ts
      • 📂 api
        • 📄 login.ts
      • 📂 ui
        • 📄 LoginPage.tsx
pages/login/api/login.ts
import { client } from 'shared/api';

interface LoginCredentials {
email: string;
password: string;
}

export function login(credentials: LoginCredentials) {
return client.post('/login', credentials);
}

คุณไม่จำเป็นต้อง Export ฟังก์ชัน login() ออกไปใน Public API ของ Page เพราะไม่น่าจะมีที่อื่นในแอปที่ต้องการ Request นี้

note

หลีกเลี่ยงการวาง API calls และ Response types ไว้ใน Layer entities เร็วเกินไป การตอบกลับจาก Backend อาจแตกต่างจากที่ Frontend entities ของคุณต้องการ Logic API ใน shared/api หรือใน api segment ของ Slice ช่วยให้คุณแปลงข้อมูลได้อย่างเหมาะสม ทำให้ Entities โฟกัสไปที่เรื่องของ Frontend เท่านั้น

การใช้ Client Generators

ถ้า Backend ของคุณมี OpenAPI specification เครื่องมืออย่าง orval หรือ openapi-typescript สามารถ Generate API types และ Request functions ให้คุณได้ วางโค้ดที่ Generate มาไว้ใน เช่น shared/api/openapi และอย่าลืมใส่ README.md เพื่ออธิบายว่า Types พวกนี้คืออะไร และจะ Generate มันยังไง

การเชื่อมต่อกับ Server State Libraries

เมื่อใช้ Server State Libraries เช่น TanStack Query (React Query) หรือ Pinia Colada คุณอาจต้องแชร์ Types หรือ Cache keys ระหว่าง Slices ให้ใช้ shared layer สำหรับสิ่งต่าง ๆ เช่น:

  • API data types
  • Cache keys
  • Common query/mutation options

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการทำงานกับ Server State Libraries ให้ดูที่ React Query article