Bỏ qua

API Integration

API Client — lib/api.ts

Tất cả API calls được tập trung trong file lib/api.ts. Sử dụng native fetch API.

const API = process.env.NEXT_PUBLIC_API

// Tạo khóa học mới
export async function createCourse(data) {
  return fetch(API + "/courses", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(data)
  }).then(r => r.json())
}

// Upload file (presigned URL flow)
export async function upload(id, file) {
  // 1. Lấy presigned URL từ backend
  const r = await fetch(API + `/courses/${id}/upload-url`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      file_name: file.name,
      content_type: file.type || "image/jpeg"
    })
  })
  const { upload_url, file_key } = await r.json()

  // 2. Upload trực tiếp lên R2
  await fetch(upload_url, { method: "PUT", body: file })

  // 3. Xác nhận upload hoàn tất
  await fetch(API + `/courses/${id}/finalize-upload`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ file_key })
  })
}

Upload Flow (3 bước)

sequenceDiagram
    participant User
    participant FE as Frontend
    participant BE as Backend
    participant R2 as Cloudflare R2

    User->>FE: Chọn file
    FE->>BE: POST /upload-url (file_name, content_type)
    BE-->>FE: { upload_url, file_key }
    FE->>R2: PUT upload_url (binary)
    R2-->>FE: 200 OK
    FE->>BE: POST /finalize-upload (file_key)
    BE-->>FE: Updated course data
    FE->>User: Hiển thị success

Tại sao 3 bước?

  • Bước 1: Backend tạo presigned URL có thời hạn (5 phút)
  • Bước 2: File đi thẳng từ browser → R2, không qua backend (nhanh hơn, tiết kiệm bandwidth)
  • Bước 3: Backend cập nhật database với key file đã upload

Quy tắc khi thêm API mới

  1. Thêm function vào lib/api.ts — giữ tất cả API calls ở 1 chỗ
  2. Luôn khai báo headersContent-Type: application/json cho JSON body
  3. Handle errors — Check response.ok trước khi parse JSON
  4. Sử dụng TanStack Query — Cho data fetching, caching, và invalidation

Template thêm API call mới:

// lib/api.ts
export async function getUsers() {
  const res = await fetch(API + "/users")
  if (!res.ok) throw new Error("Failed to fetch users")
  return res.json()
}

// Component
import { useQuery } from '@tanstack/react-query'
import { getUsers } from '@/lib/api'

const { data } = useQuery({
  queryKey: ['users'],
  queryFn: getUsers
})