11import { QueryClient , QueryFunction } from "@tanstack/react-query" ;
2+ import { auth } from "@/lib/firebase" ;
23
34async function throwIfResNotOk ( res : Response ) {
45 if ( ! res . ok ) {
@@ -7,18 +8,88 @@ async function throwIfResNotOk(res: Response) {
78 }
89}
910
11+ interface ApiRequestOptions {
12+ expectJson ?: boolean ;
13+ }
14+
1015export async function apiRequest < T = any > (
16+ method : string ,
1117 url : string ,
12- options ?: RequestInit
18+ data ?: any ,
19+ options : ApiRequestOptions = { }
1320) : Promise < T > {
21+ // Get the Firebase ID token if the user is signed in
22+ let token = null ;
23+ try {
24+ // Get the current user and their ID token
25+ const currentUser = auth . currentUser ;
26+ if ( currentUser ) {
27+ token = await currentUser . getIdToken ( ) ;
28+ console . log ( "[apiRequest] Got Firebase ID token, length:" , token . length ) ;
29+ } else {
30+ console . log ( "[apiRequest] No current user found" ) ;
31+ }
32+ } catch ( e ) {
33+ console . error ( "[apiRequest] Error getting Firebase ID token:" , e ) ;
34+ token = null ;
35+ }
36+
37+ // Build headers
38+ const headers : Record < string , string > = { } ;
39+
40+ // Only add Content-Type if we have data to send
41+ if ( data ) {
42+ headers [ "Content-Type" ] = "application/json" ;
43+ }
44+
45+ // Add Authorization header if we have a token
46+ if ( token ) {
47+ headers [ "Authorization" ] = `Bearer ${ token } ` ;
48+ console . log ( "[apiRequest] Adding Authorization header to request" ) ;
49+ } else {
50+ console . log ( "[apiRequest] No token available, making unauthenticated request" ) ;
51+ }
52+
53+ console . log ( `[apiRequest] Making ${ method } request to ${ url } ` ) ;
54+
1455 const res = await fetch ( url , {
15- headers : options ?. body ? { "Content-Type" : "application/json" } : { } ,
56+ method,
57+ headers,
58+ body : data ? JSON . stringify ( data ) : undefined ,
1659 credentials : "include" ,
17- ...options
1860 } ) ;
1961
20- await throwIfResNotOk ( res ) ;
21- return await res . json ( ) ;
62+ // Defensive: check for JSON response
63+ const contentType = res . headers . get ( "content-type" ) ;
64+
65+ // If we explicitly don't expect JSON or it's a 204 No Content, return early
66+ if ( options . expectJson === false || res . status === 204 ) {
67+ if ( ! res . ok ) {
68+ const text = ( await res . text ( ) ) || res . statusText ;
69+ throw new Error ( `${ res . status } : ${ text } ` ) ;
70+ }
71+ console . log ( `[apiRequest] ${ method } ${ url } completed successfully (no JSON response)` ) ;
72+ return undefined as T ;
73+ }
74+
75+ if ( contentType && contentType . includes ( "application/json" ) ) {
76+ const json = await res . json ( ) ;
77+ if ( ! res . ok ) {
78+ console . error ( `[apiRequest] ${ method } ${ url } failed:` , json ) ;
79+ throw new Error ( json . message || `API error: ${ res . status } ` ) ;
80+ }
81+ console . log ( `[apiRequest] ${ method } ${ url } completed successfully` ) ;
82+ return json ;
83+ } else {
84+ // Not JSON, likely an error page
85+ if ( ! res . ok ) {
86+ const text = await res . text ( ) ;
87+ console . error ( `[apiRequest] ${ method } ${ url } failed with non-JSON response:` , text . slice ( 0 , 100 ) ) ;
88+ throw new Error ( `${ res . status } : Unexpected response from server: ${ text . slice ( 0 , 100 ) } ` ) ;
89+ }
90+ const text = await res . text ( ) ;
91+ throw new Error ( "Unexpected response from server: " + text . slice ( 0 , 100 ) ) ;
92+ }
2293}
2394
2495type UnauthorizedBehavior = "returnNull" | "throw" ;
@@ -27,7 +98,24 @@ export const getQueryFn: <T>(options: {
2798} ) => QueryFunction < T > =
2899 ( { on401 : unauthorizedBehavior } ) =>
29100 async ( { queryKey } ) => {
101+ // For queries, we also need to attach the auth token
102+ let token = null ;
103+ try {
104+ const currentUser = auth . currentUser ;
105+ if ( currentUser ) {
106+ token = await currentUser . getIdToken ( ) ;
107+ }
108+ } catch ( e ) {
109+ console . error ( "[getQueryFn] Error getting Firebase ID token:" , e ) ;
110+ }
111+
112+ const headers : Record < string , string > = { } ;
113+ if ( token ) {
114+ headers [ "Authorization" ] = `Bearer ${ token } ` ;
115+ }
116+
30117 const res = await fetch ( queryKey [ 0 ] as string , {
118+ headers,
31119 credentials : "include" ,
32120 } ) ;
33121
0 commit comments