fix: auto-strip /api/ prefix in api client + document convention
The api client now calls normalizePath() to strip accidental /api/ prefixes. This prevents the recurring /api/api/ double-prefix bug. Added convention note to .claude/CLAUDE.md so future workers know.
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
- ESLint must pass before committing
|
- ESLint must pass before committing
|
||||||
- Import aliases: `@/` maps to `src/`
|
- Import aliases: `@/` maps to `src/`
|
||||||
- Bun as package manager (not npm/yarn/pnpm)
|
- Bun as package manager (not npm/yarn/pnpm)
|
||||||
|
- **API paths: NEVER include `/api/` prefix.** The `api` client in `lib/api.ts` already has `baseUrl="/api"`. Write `api.get("/cases")` NOT `api.get("/api/cases")`. The client auto-strips accidental `/api/` prefixes but don't rely on it.
|
||||||
|
|
||||||
## General
|
## General
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,14 @@ import type { ApiError } from "@/lib/types";
|
|||||||
class ApiClient {
|
class ApiClient {
|
||||||
private baseUrl = "/api";
|
private baseUrl = "/api";
|
||||||
|
|
||||||
|
/** Strip leading /api/ if accidentally included — baseUrl already provides it */
|
||||||
|
private normalizePath(path: string): string {
|
||||||
|
if (path.startsWith("/api/")) {
|
||||||
|
return path.slice(4); // "/api/foo" -> "/foo"
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
private async getHeaders(): Promise<HeadersInit> {
|
private async getHeaders(): Promise<HeadersInit> {
|
||||||
const supabase = createClient();
|
const supabase = createClient();
|
||||||
const {
|
const {
|
||||||
@@ -29,9 +37,10 @@ class ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async request<T>(
|
private async request<T>(
|
||||||
path: string,
|
rawPath: string,
|
||||||
options: RequestInit = {},
|
options: RequestInit = {},
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
|
const path = this.normalizePath(rawPath);
|
||||||
const headers = await this.getHeaders();
|
const headers = await this.getHeaders();
|
||||||
const res = await fetch(`${this.baseUrl}${path}`, {
|
const res = await fetch(`${this.baseUrl}${path}`, {
|
||||||
...options,
|
...options,
|
||||||
@@ -80,7 +89,8 @@ class ApiClient {
|
|||||||
return this.request<T>(path, { method: "DELETE" });
|
return this.request<T>(path, { method: "DELETE" });
|
||||||
}
|
}
|
||||||
|
|
||||||
async postFormData<T>(path: string, formData: FormData): Promise<T> {
|
async postFormData<T>(rawPath: string, formData: FormData): Promise<T> {
|
||||||
|
const path = this.normalizePath(rawPath);
|
||||||
const supabase = createClient();
|
const supabase = createClient();
|
||||||
const {
|
const {
|
||||||
data: { session },
|
data: { session },
|
||||||
|
|||||||
Reference in New Issue
Block a user