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:
m
2026-03-30 13:05:02 +02:00
parent 3030ef1e8b
commit 4b0ccac384
2 changed files with 13 additions and 2 deletions

View File

@@ -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

View File

@@ -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 },