Skip to content

Fix: remove duplicate /api prefix in local compose NEXT_PUBLIC_API_URL - #2

Open
mpont91 wants to merge 1 commit into
ent0n29:mainfrom
mpont91:fix/local-compose-duplicate-api-prefix
Open

Fix: remove duplicate /api prefix in local compose NEXT_PUBLIC_API_URL#2
mpont91 wants to merge 1 commit into
ent0n29:mainfrom
mpont91:fix/local-compose-duplicate-api-prefix

Conversation

@mpont91

@mpont91 mpont91 commented Aug 25, 2026

Copy link
Copy Markdown

Problem

Starting the stack with docker-compose.local.yaml as documented brings every
container up healthy, but the dashboard at localhost:3000 shows:

Failed to load leaderboard. Make sure the API server is running.

The API is in fact running and healthy. Every browser request 404s:

INFO: "GET /api/api/leaderboard?limit=100 HTTP/1.1" 404 Not Found
INFO: "GET /api/api/stats HTTP/1.1"                 404 Not Found
INFO: "GET /api/api/freshness HTTP/1.1"             404 Not Found
INFO: "GET /api/api/index/psi-10 HTTP/1.1"          404 Not Found
INFO: "GET /api/api/activity/recent?... HTTP/1.1"   404 Not Found
INFO: "GET /api/api/ml/health HTTP/1.1"             404 Not Found

Note the doubled /api/api/.

Cause

The frontend already prepends /api to every route. fetchJson() in
aware-fund/services/web/src/lib/api.ts:396 builds:

const response = await fetch(`${API_BASE}${path}`, { ... })

and callers pass paths that already carry the prefix, e.g.
fetchJson('/api/leaderboard') (line 442).

docker-compose.local.yaml set the base URL with the prefix baked in:

NEXT_PUBLIC_API_URL: http://localhost:8000/api

so the concatenation yields http://localhost:8000/api/api/leaderboard.

This is local-only: the browser talks straight to the API on port 8000 with no
reverse proxy in front to absorb the extra segment.

Fix

-      NEXT_PUBLIC_API_URL: http://localhost:8000/api  # For client-side (browser)
+      NEXT_PUBLIC_API_URL: http://localhost:8000  # For client-side (browser)

This matches what the other compose files already do:

  • deploy/docker-compose.dev.yaml:152http://localhost:8000
  • deploy/docker-compose.prod.yaml:209${NEXT_PUBLIC_API_URL:-http://api:8000}

Deliberately not changed

deploy/.env.example:57 keeps https://app.aware.fund/api. That suffix is
correct for production: deploy/nginx/nginx.conf:88 serves

location /api/ {
    proxy_pass http://api/;
}

and the trailing slash on proxy_pass strips the /api/ prefix before
forwarding, so the doubled segment is exactly what makes the backend receive
/api/leaderboard. Removing the suffix there would break production.

Verification

After the change, recreating only the web container:

/api/leaderboard?limit=100                  -> 200
/api/stats                                  -> 200
/api/freshness                              -> 200
/api/index/psi-10                           -> 200
/api/activity/recent?min_score=50&limit=20  -> 200
/api/ml/health                              -> 200

The served JS bundle now resolves the base to localhost:8000 with no
duplicate segment, and the dashboard renders. A hard refresh is needed to drop
the previously cached bundle.

The web dashboard failed to load any data when started with
docker-compose.local.yaml. Every request 404'd and the UI showed
"Failed to load leaderboard. Make sure the API server is running."
even though the API container was healthy.

The frontend already prepends /api to every route: fetchJson() in
src/lib/api.ts builds `${API_BASE}${path}` with paths such as
'/api/leaderboard'. Setting NEXT_PUBLIC_API_URL to
http://localhost:8000/api produced http://localhost:8000/api/api/...
which does not exist.

This affected the whole dashboard, not just the leaderboard:
/api/stats, /api/freshness, /api/index/psi-10, /api/activity/recent
and /api/ml/health all returned 404.

The other compose files already use the base URL without the prefix
(deploy/docker-compose.dev.yaml, deploy/docker-compose.prod.yaml), so
this aligns the local file with them.

deploy/.env.example intentionally keeps the /api suffix and is left
unchanged: in production nginx serves `location /api/` with
`proxy_pass http://api/`, whose trailing slash strips the prefix
before forwarding, so the suffix is required there to compensate.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant