1
+ /**
2
+ * AI Assistance Disclosure:
3
+ * Tool: GitHub Copilot (model: Claude Sonnet 4), date: 2025-09-16
4
+ * Purpose: To fix Docker networking bug where middleware (server-side) and browser (client-side) need different API endpoints for same service.
5
+ * Author Review: I validated correctness, security, and performance of the dynamic client creation approach.
6
+ */
7
+
1
8
// API Configuration for PeerPrep Frontend
2
9
// Based on docker-compose.yml configuration with API Gateway
3
10
import axios from 'axios' ;
@@ -16,19 +23,58 @@ export interface ApiResponse<T = unknown> {
16
23
data ?: T ;
17
24
}
18
25
19
- // Base URL - All API calls go through the API Gateway on port 80
20
- const BASE_URL = process . env . NODE_ENV === 'production'
21
- ? 'http://localhost/api' // Through API Gateway (Docker)
22
- : 'http://localhost:4000' ; // Direct to user-service (Local dev)
26
+ /**
27
+ * Dynamic Base URL Detection
28
+ *
29
+ * This function determines the correct API endpoint based on execution context:
30
+ * - Server-side (middleware): Direct container-to-container communication
31
+ * - Client-side (browser): Through API Gateway proxy
32
+ *
33
+ * This fixes a Docker networking bug where the same code runs in different environments
34
+ * and needs different URLs to reach the same service.
35
+ */
36
+ const getBaseURL = ( ) => {
37
+ // Check if we're running server-side (middleware) or client-side (browser)
38
+ const isServerSide = typeof window === 'undefined' ;
39
+
40
+ if ( process . env . NODE_ENV === 'production' ) {
41
+ if ( isServerSide ) {
42
+ // Server-side: Direct call to user-service container
43
+ // This bypasses the API gateway for internal Docker networking
44
+ return 'http://user-service:4000' ;
45
+ } else {
46
+ // Client-side: Through API Gateway
47
+ // Browser requests go through the nginx proxy on localhost
48
+ return 'http://localhost/api' ;
49
+ }
50
+ } else {
51
+ // Development: Direct to user-service (no Docker containers)
52
+ return 'http://localhost:4000' ;
53
+ }
54
+ } ;
23
55
24
- // Create axios instance with base configuration
25
- const apiClient = axios . create ( {
26
- baseURL : BASE_URL ,
27
- headers : {
28
- 'Content-Type' : 'application/json' ,
29
- } ,
30
- timeout : 10000 , // 10 seconds timeout
31
- } ) ;
56
+ /**
57
+ * Dynamic Axios Client Creation
58
+ *
59
+ * We create a new axios instance for each request instead of using a module-level singleton.
60
+ * This is necessary because:
61
+ *
62
+ * 1. The same module code runs in both server-side (middleware) and client-side (browser) contexts
63
+ * 2. Each context needs a different base URL to reach the user service
64
+ * 3. A singleton axios instance would "freeze" the URL from the first context that loads it
65
+ * 4. Dynamic creation ensures getBaseURL() runs fresh for each request context
66
+ *
67
+ * This fixes the Docker networking bug where middleware couldn't reach the user service.
68
+ */
69
+ const createApiClient = ( ) => {
70
+ return axios . create ( {
71
+ baseURL : getBaseURL ( ) , // Fresh URL calculation for current execution context
72
+ headers : {
73
+ 'Content-Type' : 'application/json' ,
74
+ } ,
75
+ timeout : 10000 , // 10 seconds timeout
76
+ } ) ;
77
+ } ;
32
78
33
79
// API Endpoints
34
80
const API_ENDPOINTS = {
@@ -44,6 +90,7 @@ const API_ENDPOINTS = {
44
90
*/
45
91
const verifyToken = async ( token : string ) => {
46
92
try {
93
+ const apiClient = createApiClient ( ) ;
47
94
const response = await apiClient . get ( `${ API_ENDPOINTS . AUTH_SERVICE } /verify-token` , {
48
95
headers : {
49
96
'Authorization' : `Bearer ${ token } `
@@ -64,6 +111,7 @@ const verifyToken = async (token: string) => {
64
111
*/
65
112
const login = async ( email : string , password : string ) => {
66
113
try {
114
+ const apiClient = createApiClient ( ) ;
67
115
const response = await apiClient . post ( `${ API_ENDPOINTS . AUTH_SERVICE } /login` , {
68
116
email,
69
117
password
@@ -84,6 +132,7 @@ const login = async (email: string, password: string) => {
84
132
*/
85
133
const signup = async ( username : string , email : string , password : string ) => {
86
134
try {
135
+ const apiClient = createApiClient ( ) ;
87
136
const response = await apiClient . post ( `${ API_ENDPOINTS . USER_SERVICE } /` , {
88
137
username,
89
138
email,
0 commit comments