Headers & Session Auth

โปรเจกต์ที่ไม่ต้องการการรับรองตัวตนให้ใช้ createHttpClient เท่านั้น และแนบ HeaderStore และ session auth เฉพาะเมื่อจำเป็น


โค้ด commonService.ts ของหน้านี้เป็นส่วนหนึ่งของไฟล์ทั้งหมด

โค้ด HeaderStore, headersProvider, sessionAuth ด้านล่างนี้ไม่ได้เป็นตัวอย่างไฟล์ที่แยกกัน แต่เป็นการอธิบายแต่ละส่วนของ HTTP adapter ที่ใช้ร่วมกันในโปรเจกต์ (rxjsHttpService.ts/commonService.ts) หากต้องการดูโครงสร้างไฟล์ทั้งหมดก่อน ให้ตรวจสอบโค้ดทั้งหมดของ HTTP Client ที่สมบูรณ์แบบ/ขยายได้แล้วอ่านหน้านี้

header ที่จะแชร์ในเบราว์เซอร์

header ที่หลายบริการต้องใช้ร่วมกัน เช่น access token ที่ได้รับหลังจากล็อกอินสามารถเก็บไว้ที่HeaderStoreHeaderStore

commonService.ts
ts
import { createHeaderStore } from "@byeolnaerim/typed-rx-http";

export const headerStore = createHeaderStore({
  "Content-Type": "application/json",
});

headerStore.merge({
  Authorization: `Bearer ${accessToken}`,
});

headerStore.remove("Authorization");

ใน Next.js จะแยก header ระหว่างเบราว์เซอร์และเซิร์ฟเวอร์

ใน Client Component จะอ่าน HeaderStore และใน Server Component จะอ่าน cookie headersProviderสามารถสร้างได้ ฟังก์ชันนี้จะแยกวิธีการดึง header ของ CSR และ SSR ออกจากกันในที่เดียว

commonService.ts
ts
const headersProvider = async () => {
  if (typeof window !== "undefined") {
    return headerStore.get();
  }

  const { cookies } = await import("next/headers");
  const accessToken = (await cookies()).get("accessToken")?.value;

  return accessToken
    ? { Authorization: `Bearer ${accessToken}` }
    : {};
};

export const service = createHttpClient<paths>({
  baseUrl: process.env.NEXT_PUBLIC_API_URL || "",
  headerStore,
  headersProvider,
});

หากต้องการ refresh 401 ให้ห่อด้วย callApi หนึ่งครั้ง

อย่าแก้ไขบริการที่สร้างขึ้นโดยอัตโนมัติทีละรายการ ให้แนบ session operator ไว้ในบริการสาธารณะcallApiจะทำให้บริการที่สร้างขึ้นทั้งหมดใช้กฎการค้นหาและ refresh โทเค็นเดียวกัน

commonService.ts
ts
import { createSessionAuth } from "@byeolnaerim/typed-rx-http";
import { defer } from "rxjs";

const sessionAuth = createSessionAuth({
  headerStore,
  tokenUrl: "/api/auth/token",
  refreshUrl: "/api/auth/token/refresh",
  logoutUrl: "/api/auth/logout",
  onLoginChange: (loggedIn) => setLogin(loggedIn),
});

export const callApi = (serviceArguments) =>
  defer(() => service.callApi(serviceArguments)).pipe(
    sessionAuth.withSessionAuth(),
  );

หากไม่มี Authorization ให้ตรวจสอบ token endpoint ก่อน และหากคำขอเดิมเป็น 401 ให้ร้องขออีกครั้งหลังจาก refresh หาก refresh ล้มเหลวให้เรียก logout endpoint และส่งต่อข้อผิดพลาดเดิม

ในกรณีที่ต้องการเพียงการซิงโครไนซ์ token โดยไม่ต้อง refresh/retry

หากไม่ใช้ 401 refresh/retry และต้องการเพียงซิงโครไนซ์ token ก่อนคำขอ ให้ใช้ withEnsureToken()

ts
return client.callApi(...).pipe(
  auth.withEnsureToken(),
);

หากไม่ต้องการการรับรองตัวตน

ts
export const { callApi, callApiStream } = createHttpClient<paths>({
  baseUrl: "/api",
});

สำหรับ API สาธารณะหรือโปรเจกต์ง่ายๆ นี่ก็เพียงพอแล้ว HeaderStore และ session auth ไม่ใช่โครงสร้างที่จำเป็น