API URL Handling
API URL Handling
Overview
This page documents the current trailing-slash and proxy behavior used by the API, frontend, and nginx layers.
The important pieces are:
- the API runs with
redirect_slashes=False - the frontend request functions in
packages/tracker-shared/src/api/*.tshardcode the correct trailing-slash form per endpoint (shared by bothtracker-frontend-svelteandtracker-admin-svelte) - nginx preserves the request path and forwards redirects where needed
Current Behavior
With redirect_slashes=False, the API accepts the path as written instead of issuing redirect responses for slash mismatches.
That means the client and proxy layers need to stay consistent about:
- endpoints that use a trailing slash
- endpoints that intentionally do not use one, such as auth routes
- forwarded headers when the app is behind HTTPS
Frontend
There is no central URL-rewriting adapter in the frontend today. The shared
request() helper in packages/tracker-shared/src/api/client.ts just
appends whatever path it's given to API_BASE_URL — it does not add,
strip, or otherwise inspect trailing slashes.
Instead, each API module under packages/tracker-shared/src/api/ hardcodes
the correct path literally, matching the backend contract at the call site.
For example, in trackers.ts, the list endpoint is called as /trackers/
(trailing slash, matching a collection route) while a single-tracker lookup
is called as `/trackers/${id}` (no trailing slash). Auth endpoints in
auth.ts (e.g. /auth/login/json, /users/me) likewise never carry a
trailing slash. Both tracker-frontend-svelte and tracker-admin-svelte
share these same request functions, so they stay consistent by construction
rather than through runtime normalization logic.
Static file URLs (e.g. /static/images/...) aren't touched by the API
client at all — they're rendered directly as <img> src values and never
passed through request().
nginx
Both tracker-frontend-svelte/docker/nginx.conf and
tracker-admin-svelte/docker/nginx.conf proxy the API through a generic
/api/ location, plus a /static/ location for served files (images, etc.):
location /static/ {
proxy_pass $api_upstream;
...
}
location /api/ {
proxy_pass $api_upstream;
proxy_redirect off;
proxy_intercept_errors on;
error_page 301 302 307 = @handle_redirect;
}
location @handle_redirect {
set $saved_redirect_location $upstream_http_location;
proxy_pass $saved_redirect_location;
}
The admin app's nginx config used to also carry ~10 exact-match location =
blocks for specific endpoints (e.g. /api/v1/auth/login/json,
/api/v1/users/me), inherited from the old React admin app. These were
confirmed redundant and removed during the Svelte cutover — the generic
/api/ block already forwards headers (Authorization, Cookie) and
handles redirects identically. See
docs/development/svelte-staging-cutover-todo.md for how that was verified.
tracker-frontend-svelte's nginx config never had the extra blocks and
needed no changes.
HTTPS
When the app runs behind a load balancer or HTTPS proxy, also keep these headers correct:
HostX-Forwarded-ForX-Forwarded-Proto
See HTTPS Behind a Load Balancer for the HTTPS-specific side of the setup.
Troubleshooting
- If the browser sees mixed-content errors, check
X-Forwarded-Protoand redirect behavior. - If the API returns unexpected redirects, check the trailing-slash rule for that endpoint.
- If nginx cannot reach the upstream, check the
Hostheader and any upstream DNS issues.